Initial commit - restart from existing code

This commit is contained in:
Tom Ray
2026-02-13 19:15:05 +01:00
parent 11df621bb2
commit 7c352bc280
1906 changed files with 491226 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
#include <regex>
#include <gtest/gtest.h>
#include <entt/config/macro.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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,540 @@
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
#include <gtest/gtest.h>
#include <entt/container/table.hpp>
#include <entt/core/iterator.hpp>
#include "../../common/config.h"
#include "../../common/linter.hpp"
#include "../../common/throwing_allocator.hpp"
TEST(Table, Constructors) {
const std::vector<int> vec_of_int{1};
const std::vector<char> vec_of_char{'a'};
entt::table<int, char> table{};
ASSERT_TRUE(table.empty());
table = entt::table<int, char>{std::allocator<void>{}};
ASSERT_TRUE(table.empty());
table = entt::table<int, char>{vec_of_int, vec_of_char};
ASSERT_EQ(table.size(), 1);
table = entt::table<int, char>{std::vector<int>{1, 2}, std::vector<char>{'a', 'b'}};
ASSERT_EQ(table.size(), 2);
table = entt::table<int, char>{vec_of_int, vec_of_char, std::allocator<void>{}};
ASSERT_EQ(table.size(), 1);
table = entt::table<int, char>{std::vector<int>{1, 2}, std::vector<char>{'a', 'b'}, std::allocator<void>{}};
ASSERT_EQ(table.size(), 2);
}
ENTT_DEBUG_TEST(TableDeathTest, Constructors) {
const std::vector<int> vec_of_int{0};
const std::vector<char> vec_of_char{};
entt::table<int, char> table{};
ASSERT_DEATH((table = entt::table<int, char>{vec_of_int, vec_of_char}), "");
ASSERT_DEATH((table = entt::table<int, char>{std::vector<int>{}, std::vector<char>{'\0'}}), "");
ASSERT_DEATH((table = entt::table<int, char>{vec_of_int, vec_of_char, std::allocator<void>{}}), "");
ASSERT_DEATH((table = entt::table<int, char>{std::vector<int>{}, std::vector<char>{'\0'}, std::allocator<void>{}}), "");
}
TEST(Table, Move) {
entt::table<int, char> table;
table.emplace(3, 'c');
static_assert(std::is_move_constructible_v<decltype(table)>, "Move constructible type required");
static_assert(std::is_move_assignable_v<decltype(table)>, "Move assignable type required");
entt::table<int, char> other{std::move(table)};
test::is_initialized(table);
ASSERT_TRUE(table.empty());
ASSERT_FALSE(other.empty());
ASSERT_EQ(other[0u], std::make_tuple(3, 'c'));
entt::table<int, char> extended{std::move(other), std::allocator<void>{}};
test::is_initialized(other);
ASSERT_TRUE(other.empty());
ASSERT_FALSE(extended.empty());
ASSERT_EQ(extended[0u], std::make_tuple(3, 'c'));
table = std::move(extended);
test::is_initialized(extended);
ASSERT_FALSE(table.empty());
ASSERT_TRUE(other.empty());
ASSERT_TRUE(extended.empty());
ASSERT_EQ(table[0u], std::make_tuple(3, 'c'));
other = entt::table<int, char>{};
other.emplace(1, 'a');
other = std::move(table);
test::is_initialized(table);
ASSERT_FALSE(table.empty());
ASSERT_FALSE(other.empty());
ASSERT_EQ(other[0u], std::make_tuple(3, 'c'));
}
TEST(Table, Swap) {
entt::table<int, char> table;
entt::table<int, char> other;
table.emplace(3, 'c');
other.emplace(1, 'a');
other.emplace(0, '\0');
other.erase(0u);
ASSERT_EQ(table.size(), 1u);
ASSERT_EQ(other.size(), 1u);
table.swap(other);
ASSERT_EQ(table.size(), 1u);
ASSERT_EQ(other.size(), 1u);
ASSERT_EQ(table[0u], std::make_tuple(0, '\0'));
ASSERT_EQ(other[0u], std::make_tuple(3, 'c'));
}
TEST(Table, Capacity) {
entt::table<int, char> table;
ASSERT_EQ(table.capacity(), 0u);
ASSERT_TRUE(table.empty());
table.reserve(64u);
ASSERT_EQ(table.capacity(), 64u);
ASSERT_TRUE(table.empty());
table.reserve(0);
ASSERT_EQ(table.capacity(), 64u);
ASSERT_TRUE(table.empty());
}
TEST(Table, ShrinkToFit) {
entt::table<int, char> table;
table.reserve(64u);
table.emplace(3, 'c');
ASSERT_EQ(table.capacity(), 64u);
ASSERT_FALSE(table.empty());
table.shrink_to_fit();
ASSERT_EQ(table.capacity(), 1u);
ASSERT_FALSE(table.empty());
table.clear();
ASSERT_EQ(table.capacity(), 1u);
ASSERT_TRUE(table.empty());
table.shrink_to_fit();
ASSERT_EQ(table.capacity(), 0u);
ASSERT_TRUE(table.empty());
}
TEST(Table, Iterator) {
using iterator = typename entt::table<int, char>::iterator;
testing::StaticAssertTypeEq<typename iterator::value_type, std::tuple<int &, char &>>();
testing::StaticAssertTypeEq<typename iterator::pointer, entt::input_iterator_pointer<std::tuple<int &, char &>>>();
testing::StaticAssertTypeEq<typename iterator::reference, std::tuple<int &, char &>>();
entt::table<int, char> table;
table.emplace(3, 'c');
iterator end{table.begin()};
iterator begin{};
begin = table.end();
std::swap(begin, end);
ASSERT_EQ(begin, table.begin());
ASSERT_EQ(end, table.end());
ASSERT_NE(begin, end);
ASSERT_EQ(begin++, table.begin());
ASSERT_EQ(begin--, table.end());
ASSERT_EQ(begin + 1, table.end());
ASSERT_EQ(end - 1, table.begin());
ASSERT_EQ(++begin, table.end());
ASSERT_EQ(--begin, table.begin());
ASSERT_EQ(begin += 1, table.end());
ASSERT_EQ(begin -= 1, table.begin());
ASSERT_EQ(begin + (end - begin), table.end());
ASSERT_EQ(begin - (begin - end), table.end());
ASSERT_EQ(end - (end - begin), table.begin());
ASSERT_EQ(end + (begin - end), table.begin());
ASSERT_EQ(begin[0u], *table.begin().operator->());
ASSERT_LT(begin, end);
ASSERT_LE(begin, table.begin());
ASSERT_GT(end, begin);
ASSERT_GE(end, table.end());
table.emplace(0, '\0');
begin = table.begin();
ASSERT_EQ(begin[0u], std::make_tuple(3, 'c'));
ASSERT_EQ(begin[1u], std::make_tuple(0, '\0'));
}
TEST(Table, ConstIterator) {
using iterator = typename entt::table<int, char>::const_iterator;
testing::StaticAssertTypeEq<typename iterator::value_type, std::tuple<const int &, const char &>>();
testing::StaticAssertTypeEq<typename iterator::pointer, entt::input_iterator_pointer<std::tuple<const int &, const char &>>>();
testing::StaticAssertTypeEq<typename iterator::reference, std::tuple<const int &, const char &>>();
entt::table<int, char> table;
table.emplace(3, 'c');
iterator cend{table.cbegin()};
iterator cbegin{};
cbegin = table.cend();
std::swap(cbegin, cend);
ASSERT_EQ(cbegin, std::as_const(table).begin());
ASSERT_EQ(cend, std::as_const(table).end());
ASSERT_EQ(cbegin, table.cbegin());
ASSERT_EQ(cend, table.cend());
ASSERT_NE(cbegin, cend);
ASSERT_EQ(cbegin++, table.cbegin());
ASSERT_EQ(cbegin--, table.cend());
ASSERT_EQ(cbegin + 1, table.cend());
ASSERT_EQ(cend - 1, table.cbegin());
ASSERT_EQ(++cbegin, table.cend());
ASSERT_EQ(--cbegin, table.cbegin());
ASSERT_EQ(cbegin += 1, table.cend());
ASSERT_EQ(cbegin -= 1, table.cbegin());
ASSERT_EQ(cbegin + (cend - cbegin), table.cend());
ASSERT_EQ(cbegin - (cbegin - cend), table.cend());
ASSERT_EQ(cend - (cend - cbegin), table.cbegin());
ASSERT_EQ(cend + (cbegin - cend), table.cbegin());
ASSERT_EQ(cbegin[0u], *table.cbegin().operator->());
ASSERT_LT(cbegin, cend);
ASSERT_LE(cbegin, table.cbegin());
ASSERT_GT(cend, cbegin);
ASSERT_GE(cend, table.cend());
table.emplace(0, '\0');
cbegin = table.cbegin();
ASSERT_EQ(cbegin[0u], std::make_tuple(3, 'c'));
ASSERT_EQ(cbegin[1u], std::make_tuple(0, '\0'));
}
TEST(Table, ReverseIterator) {
using reverse_iterator = typename entt::table<int, char>::reverse_iterator;
testing::StaticAssertTypeEq<typename reverse_iterator::value_type, std::tuple<int &, char &>>();
testing::StaticAssertTypeEq<typename reverse_iterator::pointer, entt::input_iterator_pointer<std::tuple<int &, char &>>>();
testing::StaticAssertTypeEq<typename reverse_iterator::reference, std::tuple<int &, char &>>();
entt::table<int, char> table;
table.emplace(3, 'c');
reverse_iterator end{table.rbegin()};
reverse_iterator begin{};
begin = table.rend();
std::swap(begin, end);
ASSERT_EQ(begin, table.rbegin());
ASSERT_EQ(end, table.rend());
ASSERT_NE(begin, end);
ASSERT_EQ(begin++, table.rbegin());
ASSERT_EQ(begin--, table.rend());
ASSERT_EQ(begin + 1, table.rend());
ASSERT_EQ(end - 1, table.rbegin());
ASSERT_EQ(++begin, table.rend());
ASSERT_EQ(--begin, table.rbegin());
ASSERT_EQ(begin += 1, table.rend());
ASSERT_EQ(begin -= 1, table.rbegin());
ASSERT_EQ(begin + (end - begin), table.rend());
ASSERT_EQ(begin - (begin - end), table.rend());
ASSERT_EQ(end - (end - begin), table.rbegin());
ASSERT_EQ(end + (begin - end), table.rbegin());
ASSERT_EQ(begin[0u], *table.rbegin().operator->());
ASSERT_LT(begin, end);
ASSERT_LE(begin, table.rbegin());
ASSERT_GT(end, begin);
ASSERT_GE(end, table.rend());
table.emplace(0, '\0');
begin = table.rbegin();
ASSERT_EQ(begin[0u], std::make_tuple(0, '\0'));
ASSERT_EQ(begin[1u], std::make_tuple(3, 'c'));
}
TEST(Table, ConstReverseIterator) {
using const_reverse_iterator = typename entt::table<int, char>::const_reverse_iterator;
testing::StaticAssertTypeEq<typename const_reverse_iterator::value_type, std::tuple<const int &, const char &>>();
testing::StaticAssertTypeEq<typename const_reverse_iterator::pointer, entt::input_iterator_pointer<std::tuple<const int &, const char &>>>();
testing::StaticAssertTypeEq<typename const_reverse_iterator::reference, std::tuple<const int &, const char &>>();
entt::table<int, char> table;
table.emplace(3, 'c');
const_reverse_iterator cend{table.crbegin()};
const_reverse_iterator cbegin{};
cbegin = table.crend();
std::swap(cbegin, cend);
ASSERT_EQ(cbegin, std::as_const(table).rbegin());
ASSERT_EQ(cend, std::as_const(table).rend());
ASSERT_EQ(cbegin, table.crbegin());
ASSERT_EQ(cend, table.crend());
ASSERT_NE(cbegin, cend);
ASSERT_EQ(cbegin++, table.crbegin());
ASSERT_EQ(cbegin--, table.crend());
ASSERT_EQ(cbegin + 1, table.crend());
ASSERT_EQ(cend - 1, table.crbegin());
ASSERT_EQ(++cbegin, table.crend());
ASSERT_EQ(--cbegin, table.crbegin());
ASSERT_EQ(cbegin += 1, table.crend());
ASSERT_EQ(cbegin -= 1, table.crbegin());
ASSERT_EQ(cbegin + (cend - cbegin), table.crend());
ASSERT_EQ(cbegin - (cbegin - cend), table.crend());
ASSERT_EQ(cend - (cend - cbegin), table.crbegin());
ASSERT_EQ(cend + (cbegin - cend), table.crbegin());
ASSERT_EQ(cbegin[0u], *table.crbegin().operator->());
ASSERT_LT(cbegin, cend);
ASSERT_LE(cbegin, table.crbegin());
ASSERT_GT(cend, cbegin);
ASSERT_GE(cend, table.crend());
table.emplace(0, '\0');
cbegin = table.crbegin();
ASSERT_EQ(cbegin[0u], std::make_tuple(0, '\0'));
ASSERT_EQ(cbegin[1u], std::make_tuple(3, 'c'));
}
TEST(Table, IteratorConversion) {
entt::table<int, char> table;
table.emplace(3, 'c');
const typename entt::table<int, char>::iterator it = table.begin();
typename entt::table<int, char>::const_iterator cit = it;
testing::StaticAssertTypeEq<decltype(*it), std::tuple<int &, char &>>();
testing::StaticAssertTypeEq<decltype(*cit), std::tuple<const int &, const char &>>();
ASSERT_EQ(*it.operator->(), std::make_tuple(3, 'c'));
ASSERT_EQ(*it.operator->(), *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(Table, Emplace) {
entt::table<int, char> table;
testing::StaticAssertTypeEq<decltype(table.emplace()), std::tuple<int &, char &>>();
ASSERT_EQ(table.emplace(), std::make_tuple(int{}, char{}));
ASSERT_EQ(table.emplace(3, 'c'), std::make_tuple(3, 'c'));
}
TEST(Table, Erase) {
entt::table<int, char> table;
table.emplace(3, 'c');
table.emplace(0, '\0');
table.erase(table.begin());
ASSERT_EQ(table.size(), 1u);
ASSERT_EQ(table[0u], std::make_tuple(0, '\0'));
table.emplace(3, 'c');
table.erase(1u);
ASSERT_EQ(table.size(), 1u);
ASSERT_EQ(table[0u], std::make_tuple(0, '\0'));
table.erase(0u);
ASSERT_EQ(table.size(), 0u);
}
ENTT_DEBUG_TEST(TableDeathTest, Erase) {
entt::table<int, char> table;
ASSERT_DEATH(table.erase(0u), "");
table.emplace(3, 'c');
ASSERT_DEATH(table.erase(1u), "");
}
TEST(Table, Indexing) {
entt::table<int, char> table;
table.emplace(3, 'c');
table.emplace(0, '\0');
ASSERT_EQ(table[0u], std::make_tuple(3, 'c'));
ASSERT_EQ(std::as_const(table)[1u], std::make_tuple(0, '\0'));
}
ENTT_DEBUG_TEST(TableDeathTest, Indexing) {
entt::table<int, char> table;
ASSERT_DEATH([[maybe_unused]] auto value = table[0u], "");
ASSERT_DEATH([[maybe_unused]] auto value = std::as_const(table)[0u], "");
}
TEST(Table, Clear) {
entt::table<int, char> table;
table.emplace(3, 'c');
table.emplace(0, '\0');
ASSERT_EQ(table.size(), 2u);
table.clear();
ASSERT_EQ(table.size(), 0u);
table.emplace(3, 'c');
table.emplace(0, '\0');
table.erase(0u);
ASSERT_EQ(table.size(), 1u);
table.clear();
ASSERT_EQ(table.size(), 0u);
}
TEST(Table, CustomAllocator) {
const test::throwing_allocator<void> allocator{};
entt::basic_table<std::vector<int, test::throwing_allocator<int>>, std::vector<char, test::throwing_allocator<char>>> table{allocator};
table.reserve(1u);
ASSERT_NE(table.capacity(), 0u);
table.emplace(3, 'c');
table.emplace(0, '\0');
decltype(table) other{std::move(table), allocator};
test::is_initialized(table);
ASSERT_TRUE(table.empty());
ASSERT_FALSE(other.empty());
ASSERT_NE(other.capacity(), 0u);
ASSERT_EQ(other.size(), 2u);
table = std::move(other);
test::is_initialized(other);
ASSERT_FALSE(table.empty());
ASSERT_TRUE(other.empty());
ASSERT_NE(table.capacity(), 0u);
ASSERT_EQ(table.size(), 2u);
other = {};
table.swap(other);
table = std::move(other);
test::is_initialized(other);
ASSERT_FALSE(table.empty());
ASSERT_TRUE(other.empty());
ASSERT_NE(table.capacity(), 0u);
ASSERT_EQ(table.size(), 2u);
table.clear();
ASSERT_NE(table.capacity(), 0u);
ASSERT_EQ(table.size(), 0u);
}
TEST(Table, ThrowingAllocator) {
test::throwing_allocator<void> allocator{};
entt::basic_table<std::vector<int, test::throwing_allocator<int>>, std::vector<char, test::throwing_allocator<char>>> table{allocator};
allocator.throw_counter<int>(0u);
ASSERT_THROW(table.reserve(1u), test::throwing_allocator_exception);
allocator.throw_counter<int>(0u);
allocator.throw_counter<char>(0u);
ASSERT_THROW(table.emplace(), test::throwing_allocator_exception);
ASSERT_THROW(table.emplace(3, 'c'), test::throwing_allocator_exception);
}

View File

@@ -0,0 +1,91 @@
#include <algorithm>
#include <array>
#include <vector>
#include <gtest/gtest.h>
#include <entt/core/algorithm.hpp>
#include "../../common/boxed_type.h"
TEST(Algorithm, StdSort) {
// well, I'm pretty sure it works, it's std::sort!!
std::array arr{4, 1, 3, 2, 0};
const entt::std_sort sort;
sort(arr.begin(), arr.end());
ASSERT_TRUE(std::is_sorted(arr.begin(), arr.end()));
}
TEST(Algorithm, StdSortBoxedInt) {
// well, I'm pretty sure it works, it's std::sort!!
std::array arr{test::boxed_int{4}, test::boxed_int{1}, test::boxed_int{3}, test::boxed_int{2}, test::boxed_int{0}, test::boxed_int{8}};
const entt::std_sort sort;
sort(arr.begin(), arr.end(), [](const auto &lhs, const auto &rhs) {
return lhs.value > rhs.value;
});
ASSERT_TRUE(std::is_sorted(arr.rbegin(), arr.rend()));
}
TEST(Algorithm, StdSortEmptyContainer) {
std::vector<int> vec{};
const entt::std_sort sort;
// this should crash with asan enabled if we break the constraint
sort(vec.begin(), vec.end());
}
TEST(Algorithm, InsertionSort) {
std::array arr{4, 1, 3, 2, 0};
const entt::insertion_sort sort;
sort(arr.begin(), arr.end());
ASSERT_TRUE(std::is_sorted(arr.begin(), arr.end()));
}
TEST(Algorithm, InsertionSortBoxedInt) {
std::array arr{test::boxed_int{4}, test::boxed_int{1}, test::boxed_int{3}, test::boxed_int{2}, test::boxed_int{0}, test::boxed_int{8}};
const entt::insertion_sort sort;
sort(arr.begin(), arr.end(), [](const auto &lhs, const auto &rhs) {
return lhs.value > rhs.value;
});
ASSERT_TRUE(std::is_sorted(arr.rbegin(), arr.rend()));
}
TEST(Algorithm, InsertionSortEmptyContainer) {
std::vector<int> vec{};
const 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 arr{4u, 1u, 3u, 2u, 0u};
const entt::radix_sort<8, 32> sort;
sort(arr.begin(), arr.end(), [](const auto &value) {
return value;
});
ASSERT_TRUE(std::is_sorted(arr.begin(), arr.end()));
}
TEST(Algorithm, RadixSortBoxedInt) {
std::array arr{test::boxed_int{4}, test::boxed_int{1}, test::boxed_int{3}, test::boxed_int{2}, test::boxed_int{0}, test::boxed_int{8}};
const entt::radix_sort<2, 6> sort;
sort(arr.rbegin(), arr.rend(), [](const auto &instance) {
return instance.value;
});
ASSERT_TRUE(std::is_sorted(arr.rbegin(), arr.rend()));
}
TEST(Algorithm, RadixSortEmptyContainer) {
std::vector<int> vec{};
const entt::radix_sort<8, 32> sort;
// this should crash with asan enabled if we break the constraint
sort(vec.begin(), vec.end());
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,60 @@
#include <cmath>
#include <cstddef>
#include <limits>
#include <gtest/gtest.h>
#include <entt/core/bit.hpp>
#include "../../common/config.h"
TEST(PopCount, Functionalities) {
// constexpr-ness guaranteed
constexpr auto zero_popcount = entt::popcount(0u);
ASSERT_EQ(zero_popcount, 0u);
ASSERT_EQ(entt::popcount(1u), 1u);
ASSERT_EQ(entt::popcount(2u), 1u);
ASSERT_EQ(entt::popcount(3u), 2u);
ASSERT_EQ(entt::popcount(7u), 3u);
ASSERT_EQ(entt::popcount(128u), 1u);
ASSERT_EQ(entt::popcount(201u), 4u);
}
TEST(HasSingleBit, Functionalities) {
// constexpr-ness guaranteed
constexpr auto zero_is_power_of_two = entt::has_single_bit(0u);
ASSERT_FALSE(zero_is_power_of_two);
ASSERT_TRUE(entt::has_single_bit(1u));
ASSERT_TRUE(entt::has_single_bit(2u));
ASSERT_TRUE(entt::has_single_bit(4u));
ASSERT_FALSE(entt::has_single_bit(7u));
ASSERT_TRUE(entt::has_single_bit(128u));
ASSERT_FALSE(entt::has_single_bit(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(static_cast<std::size_t>(std::pow(2, 16))), static_cast<std::size_t>(std::pow(2, 16)));
ASSERT_EQ(entt::next_power_of_two(static_cast<std::size_t>(std::pow(2, 16) + 1u)), static_cast<std::size_t>(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);
}

View File

@@ -0,0 +1,149 @@
#include <cstddef>
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
#include <gtest/gtest.h>
#include <entt/core/compressed_pair.hpp>
#include "../../common/empty.h"
#include "../../common/non_default_constructible.h"
TEST(CompressedPair, Size) {
struct local {
int value;
test::empty empty;
};
ASSERT_EQ(sizeof(entt::compressed_pair<int, int>), sizeof(int) * 2u);
ASSERT_EQ(sizeof(entt::compressed_pair<test::empty, int>), sizeof(int));
ASSERT_EQ(sizeof(entt::compressed_pair<int, test::empty>), sizeof(int));
ASSERT_LT(sizeof(entt::compressed_pair<int, test::empty>), sizeof(local));
ASSERT_LT(sizeof(entt::compressed_pair<int, test::empty>), sizeof(std::pair<int, test::empty>));
}
TEST(CompressedPair, ConstructCopyMove) {
static_assert(!std::is_default_constructible_v<entt::compressed_pair<test::non_default_constructible, test::empty>>, "Default constructible type not allowed");
static_assert(std::is_default_constructible_v<entt::compressed_pair<std::unique_ptr<int>, test::empty>>, "Default constructible type required");
static_assert(std::is_copy_constructible_v<entt::compressed_pair<test::non_default_constructible, test::empty>>, "Copy constructible type required");
static_assert(!std::is_copy_constructible_v<entt::compressed_pair<std::unique_ptr<int>, test::empty>>, "Copy constructible type not allowed");
static_assert(std::is_copy_assignable_v<entt::compressed_pair<test::non_default_constructible, test::empty>>, "Copy assignable type required");
static_assert(!std::is_copy_assignable_v<entt::compressed_pair<std::unique_ptr<int>, test::empty>>, "Copy assignable type not allowed");
static_assert(std::is_move_constructible_v<entt::compressed_pair<std::unique_ptr<int>, test::empty>>, "Move constructible type required");
static_assert(std::is_move_assignable_v<entt::compressed_pair<std::unique_ptr<int>, test::empty>>, "Move assignable type required");
entt::compressed_pair copyable{test::non_default_constructible{2}, test::empty{}};
auto by_copy{copyable};
ASSERT_EQ(by_copy.first().value, 2);
by_copy.first().value = 3;
copyable = by_copy;
ASSERT_EQ(copyable.first().value, 3);
entt::compressed_pair<test::empty, std::unique_ptr<int>> movable{test::empty{}, std::make_unique<int>(1)};
auto by_move{std::move(movable)};
ASSERT_TRUE(by_move.second());
ASSERT_EQ(*by_move.second(), 1);
*by_move.second() = 3;
movable = std::move(by_move);
ASSERT_TRUE(movable.second());
ASSERT_EQ(*movable.second(), 3);
}
TEST(CompressedPair, PiecewiseConstruct) {
const entt::compressed_pair<test::empty, test::empty> empty{std::piecewise_construct, std::make_tuple(), std::make_tuple()};
const entt::compressed_pair<std::vector<int>, std::size_t> pair{std::piecewise_construct, std::forward_as_tuple(std::vector<int>{2}), std::make_tuple(sizeof(empty))};
ASSERT_EQ(pair.first().size(), 1u);
ASSERT_EQ(pair.second(), sizeof(empty));
}
TEST(CompressedPair, DeductionGuide) {
const int value = 2;
const test::empty empty{};
entt::compressed_pair pair{value, 3};
testing::StaticAssertTypeEq<decltype(entt::compressed_pair{test::empty{}, empty}), entt::compressed_pair<test::empty, test::empty>>();
testing::StaticAssertTypeEq<decltype(pair), entt::compressed_pair<int, int>>();
ASSERT_EQ(pair.first(), 2);
ASSERT_EQ(pair.second(), 3);
}
TEST(CompressedPair, Getters) {
entt::compressed_pair pair{3, test::empty{}};
const auto &cpair = pair;
testing::StaticAssertTypeEq<decltype(pair.first()), int &>();
testing::StaticAssertTypeEq<decltype(pair.second()), test::empty &>();
testing::StaticAssertTypeEq<decltype(cpair.first()), const int &>();
testing::StaticAssertTypeEq<decltype(cpair.second()), const test::empty &>();
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);
// NOLINTNEXTLINE(readability-qualified-auto)
auto &[cfirst, csecond] = std::as_const(pair);
ASSERT_EQ(cfirst, 3);
ASSERT_EQ(csecond, 4);
testing::StaticAssertTypeEq<decltype(cfirst), const int>();
testing::StaticAssertTypeEq<decltype(csecond), const int>();
auto [tfirst, tsecond] = entt::compressed_pair{32, 64};
ASSERT_EQ(tfirst, 32);
ASSERT_EQ(tsecond, 64);
testing::StaticAssertTypeEq<decltype(cfirst), const int>();
testing::StaticAssertTypeEq<decltype(csecond), const int>();
}

View File

@@ -0,0 +1,53 @@
#include <gtest/gtest.h>
#include <entt/core/enum.hpp>
#include "../../common/bitmask.h"
template<typename Type>
struct Enum: testing::Test {
using type = Type;
};
using EnumTypes = ::testing::Types<test::enum_is_bitmask, test::enum_as_bitmask>;
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));
}

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,218 @@
#include <cstdint>
#include <string_view>
#include <type_traits>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
struct BasicHashedString: ::testing::Test {
static constexpr auto expected = std::conditional_t<
std::is_same_v<entt::id_type, std::uint32_t>,
std::integral_constant<std::uint32_t, 0xbf9cf968>,
std::integral_constant<std::uint64_t, 0x85944171f73967e8>>::value;
};
using HashedString = BasicHashedString;
using HashedWString = BasicHashedString;
TEST_F(BasicHashedString, DeductionGuide) {
testing::StaticAssertTypeEq<decltype(entt::basic_hashed_string{"foo"}), entt::hashed_string>();
testing::StaticAssertTypeEq<decltype(entt::basic_hashed_string{L"foo"}), entt::hashed_wstring>();
}
TEST_F(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);
const entt::hashed_string hs{"foobar"};
ASSERT_EQ(static_cast<hash_type>(hs), expected);
ASSERT_EQ(hs.value(), expected);
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_F(HashedString, Empty) {
using hash_type = entt::hashed_string::hash_type;
const entt::hashed_string hs{};
ASSERT_EQ(hs.size(), 0u);
ASSERT_EQ(static_cast<hash_type>(hs), entt::internal::fnv_1a_params<>::offset);
ASSERT_EQ(static_cast<const char *>(hs), nullptr);
}
TEST_F(HashedString, Correctness) {
const char *foobar = "foobar";
const std::string_view view{"foobar__", 6};
ASSERT_EQ(entt::hashed_string{foobar}, expected);
ASSERT_EQ((entt::hashed_string{view.data(), view.size()}), expected);
ASSERT_EQ(entt::hashed_string{"foobar"}, expected);
ASSERT_EQ(entt::hashed_string::value(foobar), expected);
ASSERT_EQ(entt::hashed_string::value(view.data(), view.size()), expected);
ASSERT_EQ(entt::hashed_string::value("foobar"), expected);
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_F(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_F(HashedString, Constexprness) {
using namespace entt::literals;
constexpr std::string_view view{"foobar__", 6};
ASSERT_EQ(entt::hashed_string{"quux"}, "quux"_hs);
ASSERT_EQ(entt::hashed_string{"foobar"}, expected);
ASSERT_EQ(entt::hashed_string::value("quux"), "quux"_hs);
ASSERT_EQ(entt::hashed_string::value("foobar"), expected);
ASSERT_EQ((entt::hashed_string{"quux", 4}), "quux"_hs);
ASSERT_EQ((entt::hashed_string{view.data(), view.size()}), expected);
ASSERT_EQ((entt::hashed_string::value("quux", 4)), "quux"_hs);
ASSERT_EQ((entt::hashed_string::value(view.data(), view.size())), expected);
ASSERT_LT(entt::hashed_string{"bar"}, "foo"_hs);
ASSERT_LE(entt::hashed_string{"bar"}, "bar"_hs);
ASSERT_GT(entt::hashed_string{"foo"}, "bar"_hs);
ASSERT_GE(entt::hashed_string{"foo"}, "foo"_hs);
}
TEST_F(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);
const entt::hashed_wstring hws{L"foobar"};
ASSERT_EQ(static_cast<hash_type>(hws), expected);
ASSERT_EQ(hws.value(), expected);
ASSERT_EQ(foo_hws, L"foo"_hws);
ASSERT_NE(bar_hws, L"foo"_hws);
}
TEST_F(HashedWString, Empty) {
using hash_type = entt::hashed_wstring::hash_type;
const entt::hashed_wstring hws{};
ASSERT_EQ(hws.size(), 0u);
ASSERT_EQ(static_cast<hash_type>(hws), entt::internal::fnv_1a_params<>::offset);
ASSERT_EQ(static_cast<const wchar_t *>(hws), nullptr);
}
TEST_F(HashedWString, Correctness) {
const wchar_t *foobar = L"foobar";
const std::wstring_view view{L"foobar__", 6};
ASSERT_EQ(entt::hashed_wstring{foobar}, expected);
ASSERT_EQ((entt::hashed_wstring{view.data(), view.size()}), expected);
ASSERT_EQ(entt::hashed_wstring{L"foobar"}, expected);
ASSERT_EQ(entt::hashed_wstring::value(foobar), expected);
ASSERT_EQ(entt::hashed_wstring::value(view.data(), view.size()), expected);
ASSERT_EQ(entt::hashed_wstring::value(L"foobar"), expected);
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_F(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_F(HashedWString, Constexprness) {
using namespace entt::literals;
constexpr std::wstring_view view{L"foobar__", 6};
ASSERT_EQ(entt::hashed_wstring{L"quux"}, L"quux"_hws);
ASSERT_EQ(entt::hashed_wstring{L"foobar"}, expected);
ASSERT_EQ(entt::hashed_wstring::value(L"quux"), L"quux"_hws);
ASSERT_EQ(entt::hashed_wstring::value(L"foobar"), expected);
ASSERT_EQ((entt::hashed_wstring{L"quux", 4}), L"quux"_hws);
ASSERT_EQ((entt::hashed_wstring{view.data(), view.size()}), expected);
ASSERT_EQ((entt::hashed_wstring::value(L"quux", 4)), L"quux"_hws);
ASSERT_EQ((entt::hashed_wstring::value(view.data(), view.size())), expected);
ASSERT_LT(entt::hashed_wstring{L"bar"}, L"foo"_hws);
ASSERT_LE(entt::hashed_wstring{L"bar"}, L"bar"_hws);
ASSERT_GT(entt::hashed_wstring{L"foo"}, L"bar"_hws);
ASSERT_GE(entt::hashed_wstring{L"foo"}, L"foo"_hws);
}

View File

@@ -0,0 +1,33 @@
#include <type_traits>
#include <gtest/gtest.h>
#include <entt/core/ident.hpp>
#include "../../common/boxed_type.h"
#include "../../common/empty.h"
TEST(Ident, Uniqueness) {
using id = entt::ident<test::empty, test::boxed_int>;
constexpr test::empty instance;
constexpr test::boxed_int other;
ASSERT_NE(id::value<test::empty>, id::value<test::boxed_int>);
ASSERT_EQ(id::value<test::empty>, id::value<decltype(instance)>);
ASSERT_NE(id::value<test::empty>, id::value<decltype(other)>);
ASSERT_EQ(id::value<test::empty>, id::value<test::empty>);
ASSERT_EQ(id::value<test::boxed_int>, id::value<test::boxed_int>);
// test uses in constant expressions
switch(id::value<test::boxed_int>) {
case id::value<test::boxed_int>:
SUCCEED();
break;
case id::value<test::empty>:
default:
FAIL();
break;
}
}
TEST(Identifier, SingleType) {
using id = entt::ident<test::empty>;
[[maybe_unused]] const std::integral_constant<id::value_type, id::value<test::empty>> ic;
}

View File

@@ -0,0 +1,52 @@
#include <cstddef>
#include <utility>
#include <vector>
#include <gtest/gtest.h>
#include <entt/core/iterator.hpp>
#include "../../common/boxed_type.h"
TEST(InputIteratorPointer, Functionalities) {
entt::input_iterator_pointer ptr{test::boxed_int{0}};
ASSERT_EQ(ptr->value, 0);
ptr->value = 2;
ASSERT_EQ(ptr->value, 2);
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_THROW(other = iterable);
ASSERT_NO_THROW(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[1u]}) {
ASSERT_EQ(value, 1);
}
}

View File

@@ -0,0 +1,206 @@
#include <array>
#include <cstddef>
#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) {
const std::shared_ptr<int> shared = std::make_shared<int>();
auto *plain = &*shared;
ASSERT_EQ(entt::to_address(shared), plain);
ASSERT_EQ(entt::to_address(plain), plain);
}
TEST(PoccaPocmaAndPocs, Functionalities) {
test::basic_test_allocator<int> lhs;
test::basic_test_allocator<int> rhs;
test::basic_test_allocator<int, std::false_type> no_pocs;
// code coverage purposes
ASSERT_FALSE(lhs == rhs);
ASSERT_NO_THROW(entt::propagate_on_container_swap(no_pocs, no_pocs));
// 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) {
test::basic_test_allocator<int, std::false_type> lhs;
test::basic_test_allocator<int, std::false_type> rhs;
ASSERT_DEATH(entt::propagate_on_container_swap(lhs, rhs), "");
}
TEST(AllocateUnique, Functionalities) {
test::throwing_allocator<test::throwing_type> allocator{};
allocator.throw_counter<test::throwing_type>(0u);
ASSERT_THROW((entt::allocate_unique<test::throwing_type>(allocator, false)), test::throwing_allocator_exception);
ASSERT_THROW((entt::allocate_unique<test::throwing_type>(allocator, test::throwing_type{true})), test::throwing_type_exception);
std::unique_ptr<test::throwing_type, entt::allocation_deleter<test::throwing_allocator<test::throwing_type>>> ptr = entt::allocate_unique<test::throwing_type>(allocator, false);
ASSERT_TRUE(ptr);
ASSERT_EQ(*ptr, false);
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>>>;
[[maybe_unused]] const 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>>>;
[[maybe_unused]] const 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 = 4;
const auto args = entt::uses_allocator_construction_args<int>(std::allocator<int>{}, value);
ASSERT_EQ(std::tuple_size_v<decltype(args)>, 1u);
testing::StaticAssertTypeEq<decltype(args), const std::tuple<const int &>>();
ASSERT_EQ(std::get<0>(args), value);
}
TEST(UsesAllocatorConstructionArgs, LeadingAllocatorConvention) {
const auto value = 4;
const auto args = entt::uses_allocator_construction_args<std::tuple<int, char>>(std::allocator<int>{}, value, 'c');
ASSERT_EQ(std::tuple_size_v<decltype(args)>, 4u);
testing::StaticAssertTypeEq<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 = 4u;
const auto args = entt::uses_allocator_construction_args<std::vector<int>>(std::allocator<int>{}, size);
ASSERT_EQ(std::tuple_size_v<decltype(args)>, 2u);
testing::StaticAssertTypeEq<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 = 4u;
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);
ASSERT_EQ(std::tuple_size_v<decltype(args)>, 3u);
testing::StaticAssertTypeEq<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>{});
ASSERT_EQ(std::tuple_size_v<decltype(args)>, 3u);
testing::StaticAssertTypeEq<decltype(args), const std::tuple<std::piecewise_construct_t, std::tuple<>, std::tuple<const std::allocator<int> &>>>();
}
TEST(UsesAllocatorConstructionArgs, PairValues) {
const auto size = 4u;
const auto args = entt::uses_allocator_construction_args<std::pair<int, std::vector<int>>>(std::allocator<int>{}, 3, size);
ASSERT_EQ(std::tuple_size_v<decltype(args)>, 3u);
testing::StaticAssertTypeEq<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, 4u);
const auto args = entt::uses_allocator_construction_args<std::pair<int, std::vector<int>>>(std::allocator<int>{}, value);
ASSERT_EQ(std::tuple_size_v<decltype(args)>, 3u);
testing::StaticAssertTypeEq<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)), 4u);
}
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, 4u));
ASSERT_EQ(std::tuple_size_v<decltype(args)>, 3u);
testing::StaticAssertTypeEq<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 = 4u;
test::throwing_allocator<int> allocator{};
allocator.throw_counter<int>(0u);
ASSERT_THROW((entt::make_obj_using_allocator<std::vector<int, test::throwing_allocator<int>>>(allocator, size)), test::throwing_allocator_exception);
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::array<std::byte, sizeof(int)> storage{};
const std::allocator<int> allocator{};
// NOLINTNEXTLINE(*-reinterpret-cast)
int *value = entt::uninitialized_construct_using_allocator(reinterpret_cast<int *>(storage.data()), allocator, 1);
ASSERT_EQ(*value, 1);
}
#if defined(ENTT_HAS_TRACKED_MEMORY_RESOURCE)
# include <memory_resource>
TEST(UninitializedConstructUsingAllocator, UsesAllocatorConstruction) {
using string_type = typename test::tracked_memory_resource::string_type;
test::tracked_memory_resource memory_resource{};
const std::pmr::polymorphic_allocator<string_type> allocator{&memory_resource};
alignas(string_type) std::array<std::byte, sizeof(string_type)> storage{};
// NOLINTNEXTLINE(*-reinterpret-cast)
string_type *value = entt::uninitialized_construct_using_allocator(reinterpret_cast<string_type *>(storage.data()), 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> = 2;
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, 2);
}

View File

@@ -0,0 +1,48 @@
#include <tuple>
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/tuple.hpp>
TEST(Tuple, IsTuple) {
ASSERT_FALSE(entt::is_tuple_v<int>);
ASSERT_TRUE(entt::is_tuple_v<std::tuple<>>);
ASSERT_TRUE(entt::is_tuple_v<std::tuple<int>>);
ASSERT_TRUE((entt::is_tuple_v<std::tuple<int, char>>));
}
TEST(Tuple, UnwrapTuple) {
auto single = std::make_tuple(2);
auto multi = std::make_tuple(2, 'c');
auto ref = std::forward_as_tuple(std::get<0>(single));
testing::StaticAssertTypeEq<decltype(entt::unwrap_tuple(single)), int &>();
testing::StaticAssertTypeEq<decltype(entt::unwrap_tuple(multi)), std::tuple<int, char> &>();
testing::StaticAssertTypeEq<decltype(entt::unwrap_tuple(ref)), int &>();
testing::StaticAssertTypeEq<decltype(entt::unwrap_tuple(std::move(single))), int &&>();
testing::StaticAssertTypeEq<decltype(entt::unwrap_tuple(std::move(multi))), std::tuple<int, char> &&>();
testing::StaticAssertTypeEq<decltype(entt::unwrap_tuple(std::move(ref))), int &>();
testing::StaticAssertTypeEq<decltype(entt::unwrap_tuple(std::as_const(single))), const int &>();
testing::StaticAssertTypeEq<decltype(entt::unwrap_tuple(std::as_const(multi))), const std::tuple<int, char> &>();
testing::StaticAssertTypeEq<decltype(entt::unwrap_tuple(std::as_const(ref))), int &>();
ASSERT_EQ(entt::unwrap_tuple(single), 2);
ASSERT_EQ(entt::unwrap_tuple(multi), multi);
ASSERT_EQ(entt::unwrap_tuple(std::move(ref)), 2);
}
TEST(Tuple, ForwardApply) {
entt::forward_apply first{[](auto &&...args) { return sizeof...(args); }};
entt::forward_apply second{[](int value) { return value; }};
entt::forward_apply third{[](auto... args) { return (args + ...); }};
ASSERT_EQ(first(std::make_tuple()), 0u);
ASSERT_EQ(std::as_const(first)(std::make_tuple()), 0u);
ASSERT_EQ(second(std::make_tuple(2)), 2);
ASSERT_EQ(std::as_const(second)(std::make_tuple(2)), 2);
ASSERT_EQ(third(std::make_tuple('a', 1)), 'b');
ASSERT_EQ(std::as_const(third)(std::make_tuple('a', 1)), 'b');
}

View File

@@ -0,0 +1,102 @@
#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>, "Copy constructible type required");
static_assert(std::is_move_constructible_v<entt::type_info>, "Move constructible type required");
static_assert(std::is_copy_assignable_v<entt::type_info>, "Copy assignable type required");
static_assert(std::is_move_assignable_v<entt::type_info>, "Move assignable type required");
const 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());
}
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 = 4;
ASSERT_EQ(entt::type_id(value), entt::type_id<int>());
ASSERT_EQ(entt::type_id(4), 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,279 @@
#include <functional>
#include <optional>
#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>
#include "../../common/non_comparable.h"
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 value) {
return static_cast<char>(quux = (value != 0));
}
[[nodiscard]] int bar(double, float) const {
return static_cast<int>(quux);
}
bool quux;
};
int free_function(int, const double &) {
return 64;
}
template<typename, typename Type = void>
struct multi_argument_operation {
using type = Type;
};
struct UnpackAsType: ::testing::Test {
template<typename Type, typename... Args>
static auto test_for() {
return [](entt::unpack_as_type<Type, Args>... value) {
return (value + ... + Type{});
};
}
};
struct UnpackAsValue: ::testing::Test {
template<auto Value>
static auto test_for() {
return [](auto &&...args) {
return (entt::unpack_as_value<Value, decltype(args)> + ... + 0);
};
}
};
TEST(SizeOf, Functionalities) {
ASSERT_EQ(entt::size_of_v<void>, 0u);
ASSERT_EQ(entt::size_of_v<char>, sizeof(char));
// NOLINTBEGIN(*-avoid-c-arrays)
ASSERT_EQ(entt::size_of_v<int[]>, 0u);
ASSERT_EQ(entt::size_of_v<int[3]>, sizeof(int[3]));
// NOLINTEND(*-avoid-c-arrays)
}
TEST_F(UnpackAsType, Functionalities) {
ASSERT_EQ((this->test_for<int, char, double, bool>()(1, 2, 3)), 6);
ASSERT_EQ((this->test_for<float, void, int>()(2.f, 2.2f)), 4.2f);
}
TEST_F(UnpackAsValue, Functionalities) {
ASSERT_EQ((this->test_for<2>()('c', 1., true)), 6);
ASSERT_EQ((this->test_for<true>()('c', 2.)), 2);
}
TEST(IntegralConstant, Functionalities) {
const entt::integral_constant<3> constant{};
testing::StaticAssertTypeEq<typename entt::integral_constant<3>::value_type, int>();
ASSERT_EQ(constant.value, 3);
}
TEST(Choice, Functionalities) {
static_assert(std::is_base_of_v<entt::choice_t<0>, entt::choice_t<1>>, "Base type required");
static_assert(!std::is_base_of_v<entt::choice_t<1>, entt::choice_t<0>>, "Base type not allowed");
}
TEST(TypeList, Functionalities) {
using type = entt::type_list<int, char>;
using other = entt::type_list<double>;
ASSERT_EQ(type::size, 2u);
ASSERT_EQ(other::size, 1u);
testing::StaticAssertTypeEq<decltype(type{} + other{}), entt::type_list<int, char, double>>();
testing::StaticAssertTypeEq<entt::type_list_cat_t<type, other, type, other>, entt::type_list<int, char, double, int, char, double>>();
testing::StaticAssertTypeEq<entt::type_list_cat_t<type, other>, entt::type_list<int, char, double>>();
testing::StaticAssertTypeEq<entt::type_list_cat_t<type, type>, entt::type_list<int, char, int, char>>();
testing::StaticAssertTypeEq<entt::type_list_unique_t<entt::type_list_cat_t<type, type>>, type>();
ASSERT_TRUE((entt::type_list_contains_v<type, int>));
ASSERT_TRUE((entt::type_list_contains_v<type, char>));
ASSERT_FALSE((entt::type_list_contains_v<type, double>));
testing::StaticAssertTypeEq<entt::type_list_element_t<0u, type>, int>();
testing::StaticAssertTypeEq<entt::type_list_element_t<1u, type>, char>();
testing::StaticAssertTypeEq<entt::type_list_element_t<0u, other>, double>();
ASSERT_EQ((entt::type_list_index_v<int, type>), 0u);
ASSERT_EQ((entt::type_list_index_v<char, type>), 1u);
ASSERT_EQ((entt::type_list_index_v<double, other>), 0u);
testing::StaticAssertTypeEq<entt::type_list_diff_t<entt::type_list<int, char, double>, entt::type_list<float, bool>>, entt::type_list<int, char, double>>();
testing::StaticAssertTypeEq<entt::type_list_diff_t<entt::type_list<int, char, double>, entt::type_list<int, char, double>>, entt::type_list<>>();
testing::StaticAssertTypeEq<entt::type_list_diff_t<entt::type_list<int, char, double>, entt::type_list<int, char>>, entt::type_list<double>>();
testing::StaticAssertTypeEq<entt::type_list_diff_t<entt::type_list<int, char, double>, entt::type_list<char, double>>, entt::type_list<int>>();
testing::StaticAssertTypeEq<entt::type_list_diff_t<entt::type_list<int, char, double>, entt::type_list<char>>, entt::type_list<int, double>>();
testing::StaticAssertTypeEq<entt::type_list_transform_t<entt::type_list<int, char>, entt::type_identity>, entt::type_list<int, char>>();
testing::StaticAssertTypeEq<entt::type_list_transform_t<entt::type_list<int, char>, std::add_const>, entt::type_list<const int, const char>>();
testing::StaticAssertTypeEq<entt::type_list_transform_t<entt::type_list<int, char>, multi_argument_operation>, entt::type_list<void, void>>();
ASSERT_EQ(std::tuple_size_v<entt::type_list<>>, 0u);
ASSERT_EQ(std::tuple_size_v<entt::type_list<int>>, 1u);
ASSERT_EQ((std::tuple_size_v<entt::type_list<int, float>>), 2u);
testing::StaticAssertTypeEq<int, std::tuple_element_t<0, entt::type_list<int>>>();
testing::StaticAssertTypeEq<int, std::tuple_element_t<0, entt::type_list<int, float>>>();
testing::StaticAssertTypeEq<float, std::tuple_element_t<1, entt::type_list<int, float>>>();
}
TEST(ValueList, Functionalities) {
using value = entt::value_list<0, 2>;
using other = entt::value_list<1>;
ASSERT_EQ(value::size, 2u);
ASSERT_EQ(other::size, 1u);
testing::StaticAssertTypeEq<decltype(value{} + other{}), entt::value_list<0, 2, 1>>();
testing::StaticAssertTypeEq<entt::value_list_cat_t<value, other, value, other>, entt::value_list<0, 2, 1, 0, 2, 1>>();
testing::StaticAssertTypeEq<entt::value_list_cat_t<value, other>, entt::value_list<0, 2, 1>>();
testing::StaticAssertTypeEq<entt::value_list_cat_t<value, value>, entt::value_list<0, 2, 0, 2>>();
testing::StaticAssertTypeEq<entt::value_list_unique_t<entt::value_list_cat_t<value, value>>, value>();
ASSERT_TRUE((entt::value_list_contains_v<value, 0>));
ASSERT_TRUE((entt::value_list_contains_v<value, 2>));
ASSERT_FALSE((entt::value_list_contains_v<value, 1>));
ASSERT_EQ((entt::value_list_element_v<0u, value>), 0);
ASSERT_EQ((entt::value_list_element_v<1u, value>), 2);
ASSERT_EQ((entt::value_list_element_v<0u, other>), 1);
ASSERT_EQ((entt::value_list_index_v<0, value>), 0u);
ASSERT_EQ((entt::value_list_index_v<2, value>), 1u);
ASSERT_EQ((entt::value_list_index_v<1, other>), 0u);
testing::StaticAssertTypeEq<entt::value_list_diff_t<entt::value_list<0, 1, 2>, entt::value_list<3, 4>>, entt::value_list<0, 1, 2>>();
testing::StaticAssertTypeEq<entt::value_list_diff_t<entt::value_list<0, 1, 2>, entt::value_list<0, 1, 2>>, entt::value_list<>>();
testing::StaticAssertTypeEq<entt::value_list_diff_t<entt::value_list<0, 1, 2>, entt::value_list<0, 1>>, entt::value_list<2>>();
testing::StaticAssertTypeEq<entt::value_list_diff_t<entt::value_list<0, 1, 2>, entt::value_list<1, 2>>, entt::value_list<0>>();
testing::StaticAssertTypeEq<entt::value_list_diff_t<entt::value_list<0, 1, 2>, entt::value_list<1>>, entt::value_list<0, 2>>();
ASSERT_EQ((std::tuple_size_v<entt::value_list<>>), 0u);
ASSERT_EQ((std::tuple_size_v<entt::value_list<4>>), 1u);
ASSERT_EQ((std::tuple_size_v<entt::value_list<4, 'a'>>), 2u);
testing::StaticAssertTypeEq<int, std::tuple_element_t<0, entt::value_list<4>>>();
testing::StaticAssertTypeEq<int, std::tuple_element_t<0, entt::value_list<4, 'a'>>>();
testing::StaticAssertTypeEq<char, std::tuple_element_t<1, entt::value_list<4, 'a'>>>();
}
TEST(IsApplicable, Functionalities) {
ASSERT_TRUE((entt::is_applicable_v<void(int, char), std::tuple<double, char>>));
ASSERT_FALSE((entt::is_applicable_v<void(int, char), std::tuple<int>>));
ASSERT_TRUE((entt::is_applicable_r_v<float, int(int, char), std::tuple<double, char>>));
ASSERT_FALSE((entt::is_applicable_r_v<float, void(int, char), std::tuple<double, char>>));
ASSERT_FALSE((entt::is_applicable_r_v<int, int(int, char), std::tuple<void>>));
}
TEST(IsComplete, Functionalities) {
ASSERT_FALSE(entt::is_complete_v<void>);
ASSERT_TRUE(entt::is_complete_v<int>);
}
TEST(IsIterator, Functionalities) {
ASSERT_FALSE(entt::is_iterator_v<void>);
ASSERT_FALSE(entt::is_iterator_v<int>);
ASSERT_FALSE(entt::is_iterator_v<void *>);
ASSERT_TRUE(entt::is_iterator_v<int *>);
ASSERT_TRUE(entt::is_iterator_v<std::vector<int>::iterator>);
ASSERT_TRUE(entt::is_iterator_v<std::vector<int>::const_iterator>);
ASSERT_TRUE(entt::is_iterator_v<std::vector<int>::reverse_iterator>);
}
TEST(IsEBCOEligible, Functionalities) {
ASSERT_TRUE(entt::is_ebco_eligible_v<test::non_comparable>);
ASSERT_FALSE(entt::is_ebco_eligible_v<nlohmann_json_like>);
ASSERT_FALSE(entt::is_ebco_eligible_v<double>);
ASSERT_FALSE(entt::is_ebco_eligible_v<void>);
}
TEST(IsTransparent, Functionalities) {
ASSERT_FALSE(entt::is_transparent_v<std::less<int>>);
ASSERT_TRUE(entt::is_transparent_v<std::less<void>>);
ASSERT_FALSE(entt::is_transparent_v<std::logical_not<double>>);
ASSERT_TRUE(entt::is_transparent_v<std::logical_not<void>>);
}
TEST(IsEqualityComparable, Functionalities) {
ASSERT_TRUE(entt::is_equality_comparable_v<int>);
ASSERT_TRUE(entt::is_equality_comparable_v<const int>);
ASSERT_TRUE(entt::is_equality_comparable_v<std::vector<int>>);
ASSERT_TRUE(entt::is_equality_comparable_v<std::vector<std::vector<int>>>);
ASSERT_TRUE((entt::is_equality_comparable_v<std::unordered_map<int, int>>));
ASSERT_TRUE((entt::is_equality_comparable_v<std::unordered_map<int, std::unordered_map<int, char>>>));
ASSERT_TRUE((entt::is_equality_comparable_v<std::pair<const int, int>>));
ASSERT_TRUE((entt::is_equality_comparable_v<std::pair<const int, std::unordered_map<int, char>>>));
ASSERT_TRUE(entt::is_equality_comparable_v<std::vector<test::non_comparable>::iterator>);
ASSERT_TRUE((entt::is_equality_comparable_v<std::optional<int>>));
ASSERT_TRUE(entt::is_equality_comparable_v<nlohmann_json_like>);
// NOLINTNEXTLINE(*-avoid-c-arrays)
ASSERT_FALSE(entt::is_equality_comparable_v<int[3u]>);
ASSERT_FALSE(entt::is_equality_comparable_v<test::non_comparable>);
ASSERT_FALSE(entt::is_equality_comparable_v<const test::non_comparable>);
ASSERT_FALSE(entt::is_equality_comparable_v<std::vector<test::non_comparable>>);
ASSERT_FALSE(entt::is_equality_comparable_v<std::vector<std::vector<test::non_comparable>>>);
ASSERT_FALSE((entt::is_equality_comparable_v<std::unordered_map<int, test::non_comparable>>));
ASSERT_FALSE((entt::is_equality_comparable_v<std::unordered_map<int, std::unordered_map<int, test::non_comparable>>>));
ASSERT_FALSE((entt::is_equality_comparable_v<std::pair<const int, test::non_comparable>>));
ASSERT_FALSE((entt::is_equality_comparable_v<std::pair<const int, std::unordered_map<int, test::non_comparable>>>));
ASSERT_FALSE((entt::is_equality_comparable_v<std::optional<test::non_comparable>>));
ASSERT_FALSE(entt::is_equality_comparable_v<void>);
}
TEST(ConstnessAs, Functionalities) {
testing::StaticAssertTypeEq<entt::constness_as_t<int, char>, int>();
testing::StaticAssertTypeEq<entt::constness_as_t<const int, char>, int>();
testing::StaticAssertTypeEq<entt::constness_as_t<int, const char>, const int>();
testing::StaticAssertTypeEq<entt::constness_as_t<const int, const char>, const int>();
}
TEST(MemberClass, Functionalities) {
testing::StaticAssertTypeEq<clazz, entt::member_class_t<decltype(&clazz::foo)>>();
testing::StaticAssertTypeEq<clazz, entt::member_class_t<decltype(&clazz::bar)>>();
testing::StaticAssertTypeEq<clazz, entt::member_class_t<decltype(&clazz::quux)>>();
}
TEST(NthArgument, Functionalities) {
testing::StaticAssertTypeEq<entt::nth_argument_t<0u, void(int, char, bool)>, int>();
testing::StaticAssertTypeEq<entt::nth_argument_t<1u, void(int, char, bool)>, char>();
testing::StaticAssertTypeEq<entt::nth_argument_t<2u, void(int, char, bool)>, bool>();
testing::StaticAssertTypeEq<entt::nth_argument_t<0u, decltype(&free_function)>, int>();
testing::StaticAssertTypeEq<entt::nth_argument_t<1u, decltype(&free_function)>, const double &>();
testing::StaticAssertTypeEq<entt::nth_argument_t<0u, decltype(&clazz::bar)>, double>();
testing::StaticAssertTypeEq<entt::nth_argument_t<1u, decltype(&clazz::bar)>, float>();
testing::StaticAssertTypeEq<entt::nth_argument_t<0u, decltype(&clazz::quux)>, bool>();
ASSERT_EQ(free_function(entt::nth_argument_t<0u, decltype(&free_function)>{}, entt::nth_argument_t<1u, decltype(&free_function)>{}), 64);
[[maybe_unused]] auto lambda = [value = 0u](int, float &) { return value; };
testing::StaticAssertTypeEq<entt::nth_argument_t<0u, decltype(lambda)>, int>();
testing::StaticAssertTypeEq<entt::nth_argument_t<1u, decltype(lambda)>, float &>();
}
TEST(Tag, Functionalities) {
using namespace entt::literals;
ASSERT_EQ(entt::tag<"foobar"_hs>::value, entt::hashed_string::value("foobar"));
testing::StaticAssertTypeEq<typename entt::tag<"foobar"_hs>::value_type, entt::id_type>();
}

View File

@@ -0,0 +1,62 @@
#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) {
const entt::identity identity;
int value = 2;
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;
instance.bar(0); // makes the linter happy
ASSERT_NO_THROW(entt::overload<void(int)>(&functions::foo)(0));
ASSERT_NO_THROW(entt::overload<void()>(&functions::foo)());
ASSERT_NO_THROW((instance.*entt::overload<void(int)>(&functions::bar))(0));
ASSERT_NO_THROW((instance.*entt::overload<void()>(&functions::bar))());
}
TEST(Overloaded, Functionalities) {
int iv = 0;
char cv = '\0';
const entt::overloaded func{
[&iv](int value) { iv = value; },
[&cv](char value) { cv = value; }};
func(2);
func('c');
ASSERT_EQ(iv, 2);
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,35 @@
load("@entt//bazel:copts.bzl", "COPTS")
load("@rules_cc//cc:defs.bzl", "cc_test")
# buildifier: keep sorted
_TESTS = [
"component",
"entity",
"group",
"handle",
"helper",
"organizer",
"reactive_mixin",
"registry",
"runtime_view",
"sigh_mixin",
"snapshot",
"sparse_set",
"storage",
"storage_entity",
"storage_no_instance",
"storage_utility",
"view",
]
[cc_test(
name = test,
srcs = ["{}.cpp".format(test)],
copts = COPTS,
deps = [
"//common",
"@entt",
"@googletest//:gtest",
"@googletest//:gtest_main",
],
) for test in _TESTS]

View File

@@ -0,0 +1,107 @@
#include <type_traits>
#include <gtest/gtest.h>
#include <entt/config/config.h>
#include <entt/entity/component.hpp>
#include "../../common/boxed_type.h"
#include "../../common/empty.h"
#include "../../common/entity.h"
#include "../../common/non_movable.h"
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 /*, entt::entity */> {
using entity_type = entt::entity;
using element_type = traits_based;
static constexpr auto in_place_delete = true;
static constexpr auto page_size = 8u;
};
template<>
struct entt::component_traits<traits_based, test::entity> {
using entity_type = test::entity;
using element_type = traits_based;
static constexpr auto in_place_delete = false;
static constexpr auto page_size = 16u;
};
template<typename Entity>
struct entt::component_traits<traits_based, Entity> {
using entity_type = Entity;
using element_type = traits_based;
static constexpr auto in_place_delete = true;
static constexpr auto page_size = 32u;
};
template<typename Type>
struct Component: testing::Test {
using entity_type = Type;
};
using EntityTypes = ::testing::Types<entt::entity, test::entity, test::other_entity>;
TYPED_TEST_SUITE(Component, EntityTypes, );
TYPED_TEST(Component, VoidType) {
using entity_type = typename TestFixture::entity_type;
using traits_type = entt::component_traits<void, entity_type>;
ASSERT_FALSE(traits_type::in_place_delete);
ASSERT_EQ(traits_type::page_size, 0u);
}
TYPED_TEST(Component, Empty) {
using entity_type = typename TestFixture::entity_type;
using traits_type = entt::component_traits<test::empty, entity_type>;
ASSERT_FALSE(traits_type::in_place_delete);
ASSERT_EQ(traits_type::page_size, 0u);
}
TYPED_TEST(Component, NonEmpty) {
using entity_type = typename TestFixture::entity_type;
using traits_type = entt::component_traits<test::boxed_int, entity_type>;
ASSERT_FALSE(traits_type::in_place_delete);
ASSERT_EQ(traits_type::page_size, ENTT_PACKED_PAGE);
}
TYPED_TEST(Component, NonMovable) {
using entity_type = typename TestFixture::entity_type;
using traits_type = entt::component_traits<test::non_movable, entity_type>;
ASSERT_TRUE(traits_type::in_place_delete);
ASSERT_EQ(traits_type::page_size, ENTT_PACKED_PAGE);
}
TYPED_TEST(Component, SelfContained) {
using entity_type = typename TestFixture::entity_type;
using traits_type = entt::component_traits<self_contained, entity_type>;
ASSERT_TRUE(traits_type::in_place_delete);
ASSERT_EQ(traits_type::page_size, 4u);
}
TYPED_TEST(Component, TraitsBased) {
using entity_type = typename TestFixture::entity_type;
using traits_type = entt::component_traits<traits_based, entity_type>;
if constexpr(std::is_same_v<entity_type, entt::entity>) {
ASSERT_TRUE(traits_type::in_place_delete);
ASSERT_EQ(traits_type::page_size, 8u);
} else if constexpr(std::is_same_v<entity_type, test::entity>) {
ASSERT_FALSE(traits_type::in_place_delete);
ASSERT_EQ(traits_type::page_size, 16u);
} else {
ASSERT_TRUE(traits_type::in_place_delete);
ASSERT_EQ(traits_type::page_size, 32u);
}
}

View File

@@ -0,0 +1,163 @@
#include <cstddef>
#include <cstdint>
#include <gtest/gtest.h>
#include <entt/config/config.h>
#include <entt/entity/entity.hpp>
#include "../../common/entity.h"
struct entity_traits {
using value_type = test::entity;
using entity_type = std::uint32_t;
using version_type = std::uint16_t;
static constexpr entity_type entity_mask = 0x3FFFF; // 18b
static constexpr entity_type version_mask = 0x0FFF; // 12b
};
struct other_entity_traits {
using value_type = test::other_entity;
using entity_type = std::uint32_t;
using version_type = std::uint16_t;
static constexpr entity_type entity_mask = 0xFFFFFFFF; // 32b
static constexpr entity_type version_mask = 0x00; // 0b
};
template<>
struct entt::entt_traits<test::entity>: entt::basic_entt_traits<entity_traits> {
static constexpr std::size_t page_size = ENTT_SPARSE_PAGE;
};
template<>
struct entt::entt_traits<test::other_entity>: entt::basic_entt_traits<other_entity_traits> {
static constexpr std::size_t page_size = ENTT_SPARSE_PAGE;
};
template<typename Type>
struct Entity: testing::Test {
using type = Type;
};
using EntityTypes = ::testing::Types<entt::entity, test::entity, test::other_entity>;
TYPED_TEST_SUITE(Entity, EntityTypes, );
TYPED_TEST(Entity, Traits) {
using entity_type = typename TestFixture::type;
using traits_type = entt::entt_traits<entity_type>;
constexpr entity_type tombstone{entt::tombstone};
constexpr entity_type null{entt::null};
const entity_type entity = traits_type::construct(4u, 1u);
const entity_type other = traits_type::construct(3u, 0u);
ASSERT_EQ(entt::to_integral(entity), entt::to_integral(entity));
ASSERT_NE(entt::to_integral(entity), entt::to_integral<entity_type>(entt::null));
ASSERT_NE(entt::to_integral(entity), entt::to_integral(entity_type{}));
ASSERT_EQ(entt::to_entity(entity), 4u);
ASSERT_EQ(entt::to_version(entity), !!traits_type::version_mask);
ASSERT_EQ(entt::to_entity(other), 3u);
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);
if constexpr(traits_type::version_mask == 0u) {
ASSERT_EQ(traits_type::construct(entt::to_entity(entity), entt::to_version(other)), entity);
} else {
ASSERT_NE(traits_type::construct(entt::to_entity(entity), entt::to_version(other)), 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);
ASSERT_EQ(traits_type::next(entity), traits_type::construct(entt::to_integral(entity), entt::to_version(entity) + 1u));
ASSERT_EQ(traits_type::next(other), traits_type::construct(entt::to_integral(other), entt::to_version(other) + 1u));
ASSERT_EQ(traits_type::next(entt::tombstone), traits_type::construct(entt::null, {}));
ASSERT_EQ(traits_type::next(entt::null), traits_type::construct(entt::null, {}));
if constexpr(traits_type::to_integral(tombstone) != ~typename traits_type::entity_type{}) {
// test reserved bits, if any
constexpr entity_type reserved{traits_type::to_integral(entity) | (traits_type::to_integral(tombstone) + 1u)};
ASSERT_NE(reserved, entity);
ASSERT_NE(traits_type::to_integral(null), ~typename traits_type::entity_type{});
ASSERT_NE(traits_type::to_integral(tombstone), ~typename traits_type::entity_type{});
ASSERT_EQ(traits_type::to_entity(reserved), traits_type::to_entity(entity));
ASSERT_EQ(traits_type::to_version(reserved), traits_type::to_version(entity));
ASSERT_EQ(traits_type::to_version(null), traits_type::version_mask);
ASSERT_EQ(traits_type::to_version(tombstone), traits_type::version_mask);
ASSERT_EQ(traits_type::to_version(traits_type::next(null)), 0u);
ASSERT_EQ(traits_type::to_version(traits_type::next(tombstone)), 0u);
ASSERT_EQ(traits_type::construct(traits_type::to_integral(entity), traits_type::version_mask + 1u), entity_type{traits_type::to_entity(entity)});
ASSERT_EQ(traits_type::construct(traits_type::to_integral(null), traits_type::to_version(null) + 1u), entity_type{traits_type::to_entity(null)});
ASSERT_EQ(traits_type::construct(traits_type::to_integral(tombstone), traits_type::to_version(tombstone) + 1u), entity_type{traits_type::to_entity(tombstone)});
ASSERT_EQ(traits_type::next(reserved), traits_type::next(entity));
ASSERT_EQ(traits_type::next(null), traits_type::combine(null, entity_type{}));
ASSERT_EQ(traits_type::next(tombstone), traits_type::combine(tombstone, entity_type{}));
ASSERT_EQ(traits_type::combine(entity, reserved), entity);
ASSERT_NE(traits_type::combine(entity, reserved), reserved);
ASSERT_EQ(traits_type::combine(reserved, entity), entity);
ASSERT_NE(traits_type::combine(reserved, entity), reserved);
}
}
TYPED_TEST(Entity, Null) {
using entity_type = typename TestFixture::type;
using traits_type = entt::entt_traits<entity_type>;
constexpr entity_type null{entt::null};
ASSERT_FALSE(entity_type{} == entt::null);
ASSERT_TRUE(entt::null == entt::null);
ASSERT_FALSE(entt::null != entt::null);
const entity_type entity{4u};
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);
ASSERT_FALSE(entity == entt::null);
ASSERT_FALSE(entt::null == entity);
ASSERT_TRUE(entity != entt::null);
ASSERT_TRUE(entt::null != entity);
}
TYPED_TEST(Entity, Tombstone) {
using entity_type = typename TestFixture::type;
using traits_type = entt::entt_traits<entity_type>;
constexpr entity_type tombstone{entt::tombstone};
ASSERT_FALSE(entity_type{} == entt::tombstone);
ASSERT_TRUE(entt::tombstone == entt::tombstone);
ASSERT_FALSE(entt::tombstone != entt::tombstone);
const entity_type entity{4u};
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);
ASSERT_FALSE(entity == entt::tombstone);
ASSERT_FALSE(entt::tombstone == entity);
ASSERT_TRUE(entity != entt::tombstone);
ASSERT_TRUE(entt::tombstone != entity);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,620 @@
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/type_info.hpp>
#include <entt/core/type_traits.hpp>
#include <entt/entity/entity.hpp>
#include <entt/entity/handle.hpp>
#include <entt/entity/mixin.hpp>
#include <entt/entity/registry.hpp>
#include <entt/entity/storage.hpp>
#include "../../common/config.h"
template<typename Type>
struct BasicHandle: testing::Test {
using type = Type;
};
template<typename Type>
using BasicHandleDeathTest = BasicHandle<Type>;
using BasicHandleTypes = ::testing::Types<entt::handle, entt::const_handle>;
TYPED_TEST_SUITE(BasicHandle, BasicHandleTypes, );
TYPED_TEST_SUITE(BasicHandleDeathTest, BasicHandleTypes, );
TYPED_TEST(BasicHandle, Assumptions) {
using handle_type = typename TestFixture::type;
static_assert(std::is_trivially_copyable_v<handle_type>, "Trivially copyable type required");
static_assert((std::is_trivially_assignable_v<handle_type, handle_type>), "Trivially assignable type required");
static_assert(std::is_trivially_destructible_v<handle_type>, "Trivially destructible type required");
}
TYPED_TEST(BasicHandle, DeductionGuide) {
using handle_type = typename TestFixture::type;
testing::StaticAssertTypeEq<decltype(entt::basic_handle{std::declval<typename handle_type::registry_type &>(), {}}), handle_type>();
}
TYPED_TEST(BasicHandle, Construction) {
using handle_type = typename TestFixture::type;
entt::registry registry;
const auto entity = registry.create();
handle_type handle{};
ASSERT_FALSE(handle);
ASSERT_FALSE(handle.valid());
ASSERT_TRUE(handle == entt::null);
ASSERT_EQ(handle.registry(), nullptr);
ASSERT_NE(handle, (entt::handle{registry, entity}));
ASSERT_NE(handle, (entt::const_handle{registry, entity}));
handle = handle_type{registry, entity};
ASSERT_TRUE(handle);
ASSERT_TRUE(handle.valid());
ASSERT_FALSE(handle == entt::null);
ASSERT_EQ(handle.registry(), &registry);
ASSERT_EQ(handle, (entt::handle{registry, entity}));
ASSERT_EQ(handle, (entt::const_handle{registry, entity}));
handle = {};
ASSERT_FALSE(handle);
ASSERT_FALSE(handle.valid());
ASSERT_TRUE(handle == entt::null);
ASSERT_EQ(handle.registry(), nullptr);
ASSERT_NE(handle, (entt::handle{registry, entity}));
ASSERT_NE(handle, (entt::const_handle{registry, entity}));
}
TYPED_TEST(BasicHandle, Storage) {
using handle_type = typename TestFixture::type;
entt::registry registry;
const auto entity = registry.create();
const handle_type handle{registry, entity};
testing::StaticAssertTypeEq<decltype(*handle.storage().begin()), std::pair<entt::id_type, entt::constness_as_t<entt::sparse_set, typename handle_type::registry_type> &>>();
ASSERT_EQ(handle.storage().begin(), handle.storage().end());
registry.storage<double>();
registry.emplace<int>(entity);
ASSERT_NE(handle.storage().begin(), handle.storage().end());
ASSERT_EQ(++handle.storage().begin(), handle.storage().end());
ASSERT_EQ(handle.storage().begin()->second.type(), entt::type_id<int>());
}
ENTT_DEBUG_TYPED_TEST(BasicHandleDeathTest, Storage) {
using handle_type = typename TestFixture::type;
const handle_type handle{};
ASSERT_DEATH([[maybe_unused]] auto iterable = handle.storage(), "");
}
TYPED_TEST(BasicHandle, HandleStorageIterator) {
using handle_type = typename TestFixture::type;
entt::registry registry;
const auto entity = registry.create();
registry.emplace<int>(entity);
registry.emplace<double>(entity);
// required to test the find-first initialization step
registry.storage<entt::entity>().erase(entity);
const handle_type handle{registry, entity};
auto iterable = handle.storage();
ASSERT_FALSE(registry.valid(entity));
ASSERT_FALSE(handle);
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());
}
TYPED_TEST(BasicHandle, Entity) {
using handle_type = typename TestFixture::type;
entt::registry registry;
const auto entity = registry.create();
handle_type handle{};
ASSERT_TRUE(handle == entt::null);
ASSERT_NE(handle.entity(), entity);
ASSERT_NE(handle, entity);
handle = handle_type{registry, entity};
ASSERT_FALSE(handle == entt::null);
ASSERT_EQ(handle.entity(), entity);
ASSERT_EQ(handle, entity);
}
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_EQ(handle.registry(), &registry);
ASSERT_EQ(handle.entity(), entity);
handle.destroy(traits_type::to_version(entity));
ASSERT_FALSE(handle);
ASSERT_EQ(handle.registry(), &registry);
ASSERT_EQ(handle.entity(), entt::entity{entt::null});
ASSERT_EQ(registry.current(entity), traits_type::to_version(entity));
handle = entt::handle{registry, registry.create()};
ASSERT_TRUE(handle);
ASSERT_EQ(handle.registry(), &registry);
ASSERT_EQ(handle.entity(), entity);
handle.destroy();
ASSERT_FALSE(handle);
ASSERT_NE(handle.registry(), nullptr);
ASSERT_NE(registry.current(entity), traits_type::to_version(entity));
ASSERT_EQ(handle.entity(), entt::entity{entt::null});
}
ENTT_DEBUG_TEST(BasicHandleDeathTest, Destruction) {
entt::handle handle{};
ASSERT_DEATH(handle.destroy(0u);, "");
ASSERT_DEATH(handle.destroy();, "");
}
TEST(BasicHandle, Emplace) {
entt::registry registry;
const auto entity = registry.create();
const entt::handle handle{registry, entity};
ASSERT_FALSE(registry.all_of<int>(entity));
ASSERT_EQ(handle.emplace<int>(3), 3);
ASSERT_TRUE(registry.all_of<int>(entity));
ASSERT_EQ(registry.get<int>(entity), 3);
}
ENTT_DEBUG_TEST(BasicHandleDeathTest, Emplace) {
const entt::handle handle{};
ASSERT_DEATH(handle.emplace<int>(3);, "");
}
TEST(BasicHandle, EmplaceOrReplace) {
entt::registry registry;
const auto entity = registry.create();
const entt::handle handle{registry, entity};
ASSERT_FALSE(registry.all_of<int>(entity));
ASSERT_EQ(handle.emplace_or_replace<int>(3), 3);
ASSERT_TRUE(registry.all_of<int>(entity));
ASSERT_EQ(registry.get<int>(entity), 3);
ASSERT_EQ(handle.emplace_or_replace<int>(1), 1);
ASSERT_EQ(registry.get<int>(entity), 1);
}
ENTT_DEBUG_TEST(BasicHandleDeathTest, EmplaceOrReplace) {
const entt::handle handle{};
ASSERT_DEATH(handle.emplace_or_replace<int>(3);, "");
}
TEST(BasicHandle, Patch) {
entt::registry registry;
const auto entity = registry.create();
const entt::handle handle{registry, entity};
registry.emplace<int>(entity, 3);
ASSERT_TRUE(handle.all_of<int>());
ASSERT_EQ(handle.patch<int>([](auto &comp) { comp = 1; }), 1);
ASSERT_EQ(registry.get<int>(entity), 1);
}
ENTT_DEBUG_TEST(BasicHandleDeathTest, Patch) {
const entt::handle handle{};
ASSERT_DEATH(handle.patch<int>([](auto &comp) { comp = 1; });, "");
}
TEST(BasicHandle, Replace) {
entt::registry registry;
const auto entity = registry.create();
const entt::handle handle{registry, entity};
registry.emplace<int>(entity, 3);
ASSERT_TRUE(handle.all_of<int>());
ASSERT_EQ(handle.replace<int>(1), 1);
ASSERT_EQ(registry.get<int>(entity), 1);
}
ENTT_DEBUG_TEST(BasicHandleDeathTest, Replace) {
const entt::handle handle{};
ASSERT_DEATH(handle.replace<int>(3);, "");
}
TEST(BasicHandle, Remove) {
entt::registry registry;
const auto entity = registry.create();
const entt::handle handle{registry, entity};
ASSERT_FALSE(handle.all_of<int>());
ASSERT_EQ(handle.remove<int>(), 0u);
registry.emplace<int>(entity, 3);
ASSERT_TRUE(handle.all_of<int>());
ASSERT_EQ(handle.remove<int>(), 1u);
ASSERT_FALSE(handle.all_of<int>());
ASSERT_EQ(handle.remove<int>(), 0u);
}
ENTT_DEBUG_TEST(BasicHandleDeathTest, Remove) {
const entt::handle handle{};
ASSERT_DEATH(handle.remove<int>();, "");
}
TEST(BasicHandle, Erase) {
entt::registry registry;
const auto entity = registry.create();
const entt::handle handle{registry, entity};
registry.emplace<int>(entity, 3);
ASSERT_TRUE(handle.all_of<int>());
handle.erase<int>();
ASSERT_FALSE(handle.all_of<int>());
}
ENTT_DEBUG_TEST(BasicHandleDeathTest, Erase) {
const entt::handle handle{};
ASSERT_DEATH(handle.erase<int>();, "");
}
TYPED_TEST(BasicHandle, AllAnyOf) {
entt::registry registry;
const auto entity = registry.create();
const entt::handle handle{registry, entity};
ASSERT_FALSE((handle.all_of<int, char>()));
ASSERT_FALSE((handle.any_of<int, char>()));
registry.emplace<char>(entity);
ASSERT_FALSE((handle.all_of<int, char>()));
ASSERT_TRUE((handle.any_of<int, char>()));
registry.emplace<int>(entity);
ASSERT_TRUE((handle.all_of<int, char>()));
ASSERT_TRUE((handle.any_of<int, char>()));
}
ENTT_DEBUG_TYPED_TEST(BasicHandleDeathTest, AllAnyOf) {
using handle_type = typename TestFixture::type;
const handle_type handle{};
ASSERT_DEATH([[maybe_unused]] const auto all_of = handle.template all_of<int>(), "");
ASSERT_DEATH([[maybe_unused]] const auto any_of = handle.template any_of<int>(), "");
}
TYPED_TEST(BasicHandle, Get) {
entt::registry registry;
const auto entity = registry.create();
const entt::handle handle{registry, entity};
registry.emplace<int>(entity, 3);
registry.emplace<char>(entity, 'c');
ASSERT_EQ(handle.get<int>(), 3);
ASSERT_EQ((handle.get<int, const char>()), (std::make_tuple(3, 'c')));
std::get<0>(handle.get<int, char>()) = 1;
std::get<1>(handle.get<int, char>()) = '\0';
ASSERT_EQ(registry.get<int>(entity), 1);
ASSERT_EQ(registry.get<char>(entity), '\0');
}
ENTT_DEBUG_TYPED_TEST(BasicHandleDeathTest, Get) {
using handle_type = typename TestFixture::type;
const handle_type handle{};
ASSERT_DEATH([[maybe_unused]] const auto &elem = handle.template get<int>(), "");
}
TEST(BasicHandle, GetOrEmplace) {
entt::registry registry;
const auto entity = registry.create();
const entt::handle handle{registry, entity};
ASSERT_FALSE(registry.all_of<int>(entity));
ASSERT_EQ(handle.get_or_emplace<int>(3), 3);
ASSERT_TRUE(registry.all_of<int>(entity));
ASSERT_EQ(registry.get<int>(entity), 3);
ASSERT_EQ(handle.get_or_emplace<int>(1), 3);
}
ENTT_DEBUG_TEST(BasicHandleDeathTest, GetOrEmplace) {
const entt::handle handle{};
ASSERT_DEATH([[maybe_unused]] auto &&elem = handle.template get_or_emplace<int>(3), "");
}
TYPED_TEST(BasicHandle, TryGet) {
entt::registry registry;
const auto entity = registry.create();
const entt::handle handle{registry, entity};
ASSERT_EQ((handle.try_get<int, const char>()), (std::make_tuple(nullptr, nullptr)));
registry.emplace<int>(entity, 3);
ASSERT_NE(handle.try_get<int>(), nullptr);
ASSERT_EQ(handle.try_get<char>(), nullptr);
ASSERT_EQ((*std::get<0>(handle.try_get<int, const char>())), 3);
ASSERT_EQ((std::get<1>(handle.try_get<int, const char>())), nullptr);
*std::get<0>(handle.try_get<int, const char>()) = 1;
ASSERT_EQ(registry.get<int>(entity), 1);
}
ENTT_DEBUG_TYPED_TEST(BasicHandleDeathTest, TryGet) {
using handle_type = typename TestFixture::type;
const handle_type handle{};
ASSERT_DEATH([[maybe_unused]] const auto *elem = handle.template try_get<int>(), "");
}
TYPED_TEST(BasicHandle, Orphan) {
entt::registry registry;
const auto entity = registry.create();
const entt::handle handle{registry, entity};
ASSERT_TRUE(handle.orphan());
registry.emplace<int>(entity);
registry.emplace<char>(entity);
ASSERT_FALSE(handle.orphan());
registry.erase<char>(entity);
ASSERT_FALSE(handle.orphan());
registry.erase<int>(entity);
ASSERT_TRUE(handle.orphan());
}
ENTT_DEBUG_TYPED_TEST(BasicHandleDeathTest, Orphan) {
using handle_type = typename TestFixture::type;
const handle_type handle{};
ASSERT_DEATH([[maybe_unused]] const auto result = handle.orphan(), "");
}
/*
TEST(BasicHandle, Component) {
entt::registry registry;
const auto entity = registry.create();
const 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 = 2; });
ASSERT_EQ(2, patched);
ASSERT_EQ('a', handle.replace<char>('a'));
ASSERT_TRUE((handle.all_of<int, char, double>()));
ASSERT_EQ((std::make_tuple(2, '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(2, handle.get_or_emplace<int>(2));
ASSERT_EQ(2, handle.get_or_emplace<int>(1));
ASSERT_EQ(2, handle.get<int>());
ASSERT_EQ(2, *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, ImplicitConversion) {
entt::registry registry;
const entt::handle handle{registry, registry.create()};
const entt::const_handle const_handle = handle;
const entt::handle_view<int, char> handle_view = handle;
const entt::const_handle_view<int> const_handle_view = handle_view;
handle.emplace<int>(2);
ASSERT_EQ(handle.get<int>(), const_handle.get<int>());
ASSERT_EQ(const_handle.get<int>(), handle_view.get<int>());
ASSERT_EQ(handle_view.get<int>(), const_handle_view.get<int>());
ASSERT_EQ(const_handle_view.get<int>(), 2);
}
TYPED_TEST(BasicHandle, Comparison) {
using handle_type = typename TestFixture::type;
handle_type handle{};
ASSERT_EQ(handle, entt::handle{});
ASSERT_TRUE(handle == entt::handle{});
ASSERT_FALSE(handle != entt::handle{});
ASSERT_EQ(handle, entt::const_handle{});
ASSERT_TRUE(handle == entt::const_handle{});
ASSERT_FALSE(handle != entt::const_handle{});
entt::registry registry;
const auto entity = registry.create();
handle = handle_type{registry, entity};
ASSERT_NE(handle, entt::handle{});
ASSERT_FALSE(handle == entt::handle{});
ASSERT_TRUE(handle != entt::handle{});
ASSERT_NE(handle, entt::const_handle{});
ASSERT_FALSE(handle == entt::const_handle{});
ASSERT_TRUE(handle != entt::const_handle{});
handle = {};
ASSERT_EQ(handle, entt::handle{});
ASSERT_TRUE(handle == entt::handle{});
ASSERT_FALSE(handle != entt::handle{});
ASSERT_EQ(handle, entt::const_handle{});
ASSERT_TRUE(handle == entt::const_handle{});
ASSERT_FALSE(handle != entt::const_handle{});
entt::registry diff;
handle = {registry, entity};
const handle_type other = {diff, diff.create()};
ASSERT_NE(handle, other);
ASSERT_FALSE(other == handle);
ASSERT_TRUE(other != handle);
ASSERT_EQ(handle.entity(), other.entity());
ASSERT_NE(handle.registry(), other.registry());
}
TYPED_TEST(BasicHandle, Null) {
using handle_type = typename TestFixture::type;
handle_type handle{};
ASSERT_TRUE(handle == entt::null);
ASSERT_TRUE(entt::null == handle);
ASSERT_FALSE(handle != entt::null);
ASSERT_FALSE(entt::null != handle);
entt::registry registry;
const auto entity = registry.create();
handle = handle_type{registry, entity};
ASSERT_FALSE(handle == entt::null);
ASSERT_FALSE(entt::null == handle);
ASSERT_TRUE(handle != entt::null);
ASSERT_TRUE(entt::null != handle);
if constexpr(!std::is_const_v<typename handle_type::registry_type>) {
handle.destroy();
ASSERT_TRUE(handle == entt::null);
ASSERT_TRUE(entt::null == handle);
ASSERT_FALSE(handle != entt::null);
ASSERT_FALSE(entt::null != handle);
}
}
TYPED_TEST(BasicHandle, FromEntity) {
using handle_type = typename TestFixture::type;
entt::registry registry;
const auto entity = registry.create();
registry.emplace<int>(entity, 2);
registry.emplace<char>(entity, 'c');
const handle_type handle{registry, entity};
ASSERT_TRUE(handle);
ASSERT_EQ(entity, handle.entity());
ASSERT_TRUE((handle.template all_of<int, char>()));
ASSERT_EQ(handle.template get<int>(), 2);
ASSERT_EQ(handle.template get<char>(), 'c');
}
TEST(BasicHandle, Lifetime) {
entt::registry registry;
const auto entity = registry.create();
auto handle = std::make_unique<entt::handle>(registry, entity);
handle->emplace<int>();
ASSERT_FALSE(registry.storage<int>().empty());
ASSERT_NE(registry.storage<entt::entity>().free_list(), 0u);
for(auto [entt]: registry.storage<entt::entity>().each()) {
ASSERT_EQ(handle->entity(), entt);
}
handle.reset();
ASSERT_FALSE(registry.storage<int>().empty());
ASSERT_NE(registry.storage<entt::entity>().free_list(), 0u);
}

View File

@@ -0,0 +1,162 @@
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/entity/component.hpp>
#include <entt/entity/entity.hpp>
#include <entt/entity/helper.hpp>
#include <entt/entity/mixin.hpp>
#include <entt/entity/registry.hpp>
#include <entt/entity/storage.hpp>
#include <entt/entity/view.hpp>
#include <entt/signal/sigh.hpp>
#include "../../common/pointer_stable.h"
struct clazz {
void func(entt::registry &, entt::entity curr) {
entt = curr;
}
entt::entity entt{entt::null};
};
void sigh_callback(int &value) {
++value;
}
template<typename Type>
struct ToEntity: testing::Test {
using type = Type;
};
template<typename Type>
using ToEntityDeprecated = ToEntity<Type>;
using ToEntityTypes = ::testing::Types<int, test::pointer_stable>;
TYPED_TEST_SUITE(ToEntity, ToEntityTypes, );
TYPED_TEST_SUITE(ToEntityDeprecated, ToEntityTypes, );
TEST(AsView, Functionalities) {
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(AsGroup, Functionalities) {
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(Invoke, Functionalities) {
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);
}
TYPED_TEST(ToEntity, Functionalities) {
using value_type = typename TestFixture::type;
using traits_type = entt::component_traits<value_type>;
entt::registry registry;
const entt::entity null = entt::null;
auto &storage = registry.storage<value_type>();
const value_type value{4};
ASSERT_EQ(entt::to_entity(storage, value), null);
const auto entity = registry.create();
storage.emplace(entity);
while(storage.size() < (traits_type::page_size - (1u + traits_type::in_place_delete))) {
storage.emplace(registry.create(), value);
}
const auto other = registry.create();
const auto next = registry.create();
storage.emplace(other);
storage.emplace(next);
ASSERT_EQ(entt::to_entity(storage, storage.get(entity)), entity);
ASSERT_EQ(entt::to_entity(storage, storage.get(other)), other);
ASSERT_EQ(entt::to_entity(storage, storage.get(next)), next);
ASSERT_EQ(*storage.entt::sparse_set::rbegin(), entity);
ASSERT_EQ(&*(storage.rbegin() + traits_type::page_size - (1u + traits_type::in_place_delete)), &storage.get(other));
// erase in the middle
storage.erase(other);
ASSERT_EQ(entt::to_entity(storage, storage.get(entity)), entity);
ASSERT_EQ(entt::to_entity(storage, storage.get(next)), next);
ASSERT_EQ(*storage.entt::sparse_set::rbegin(), entity);
ASSERT_EQ(&*(storage.rbegin() + traits_type::page_size - 1u), &storage.get(next));
ASSERT_EQ(entt::to_entity(storage, value), null);
storage.clear();
storage.emplace(entity);
storage.emplace(other);
storage.emplace(next);
// erase first
storage.erase(entity);
ASSERT_EQ(entt::to_entity(storage, value), null);
ASSERT_EQ(entt::to_entity(storage, storage.get(other)), other);
}
TEST(SighHelper, Functionalities) {
using namespace entt::literals;
entt::registry registry{};
const auto entt = registry.create();
entt::sigh_helper helper{registry};
int counter{};
ASSERT_EQ(&helper.registry(), &registry);
helper.with<int>()
.on_construct<&sigh_callback>(counter)
.on_update<&sigh_callback>(counter)
.on_destroy<&sigh_callback>(counter);
ASSERT_EQ(counter, 0);
registry.emplace<int>(entt);
registry.replace<int>(entt);
registry.erase<int>(entt);
ASSERT_EQ(counter, 3);
helper.with<double>("other"_hs)
.on_construct<&sigh_callback>(counter)
.on_update<&sigh_callback>(counter)
.on_destroy<&sigh_callback>(counter);
registry.emplace<double>(entt);
registry.replace<double>(entt);
registry.erase<double>(entt);
ASSERT_EQ(counter, 3);
registry.storage<double>("other"_hs).emplace(entt);
registry.storage<double>("other"_hs).patch(entt);
registry.storage<double>("other"_hs).erase(entt);
ASSERT_EQ(counter, 6);
}

View File

@@ -0,0 +1,505 @@
#include <array>
#include <cstddef>
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/type_info.hpp>
#include <entt/entity/group.hpp>
#include <entt/entity/mixin.hpp>
#include <entt/entity/organizer.hpp>
#include <entt/entity/registry.hpp>
#include <entt/entity/view.hpp>
void ro_int_rw_char_double(entt::view<entt::get_t<const int, char>>, double &) {}
void ro_char_rw_int(entt::group<entt::owned_t<int>, entt::get_t<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::group<entt::owned_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 &) {
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].in_edges().size(), 0u);
ASSERT_EQ(graph[1u].in_edges().size(), 1u);
ASSERT_EQ(graph[2u].in_edges().size(), 1u);
ASSERT_EQ(graph[3u].in_edges().size(), 2u);
ASSERT_EQ(graph[1u].in_edges()[0u], 0u);
ASSERT_EQ(graph[2u].in_edges()[0u], 0u);
ASSERT_EQ(graph[3u].in_edges()[0u], 1u);
ASSERT_EQ(graph[3u].in_edges()[1u], 2u);
ASSERT_EQ(graph[0u].out_edges().size(), 2u);
ASSERT_EQ(graph[1u].out_edges().size(), 1u);
ASSERT_EQ(graph[2u].out_edges().size(), 1u);
ASSERT_EQ(graph[3u].out_edges().size(), 0u);
ASSERT_EQ(graph[0u].out_edges()[0u], 1u);
ASSERT_EQ(graph[0u].out_edges()[1u], 2u);
ASSERT_EQ(graph[1u].out_edges()[0u], 3u);
ASSERT_EQ(graph[2u].out_edges()[0u], 3u);
for(auto &&vertex: graph) {
typename entt::organizer::function_type *cb = vertex.callback();
ASSERT_NO_THROW(cb(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].in_edges().size(), 0u);
ASSERT_EQ(graph[1u].in_edges().size(), 1u);
ASSERT_EQ(graph[2u].in_edges().size(), 1u);
ASSERT_EQ(graph[3u].in_edges().size(), 1u);
ASSERT_EQ(graph[1u].in_edges()[0u], 0u);
ASSERT_EQ(graph[2u].in_edges()[0u], 1u);
ASSERT_EQ(graph[3u].in_edges()[0u], 2u);
ASSERT_EQ(graph[0u].out_edges().size(), 1u);
ASSERT_EQ(graph[1u].out_edges().size(), 1u);
ASSERT_EQ(graph[2u].out_edges().size(), 1u);
ASSERT_EQ(graph[3u].out_edges().size(), 0u);
ASSERT_EQ(graph[0u].out_edges()[0u], 1u);
ASSERT_EQ(graph[1u].out_edges()[0u], 2u);
ASSERT_EQ(graph[2u].out_edges()[0u], 3u);
for(auto &&vertex: graph) {
typename entt::organizer::function_type *cb = vertex.callback();
ASSERT_NO_THROW(cb(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[3u].in_edges().size(), 1u);
ASSERT_EQ(graph[4u].in_edges().size(), 3u);
ASSERT_EQ(graph[3u].in_edges()[0u], 2u);
ASSERT_EQ(graph[4u].in_edges()[0u], 0u);
ASSERT_EQ(graph[4u].in_edges()[1u], 1u);
ASSERT_EQ(graph[4u].in_edges()[2u], 3u);
ASSERT_EQ(graph[0u].out_edges().size(), 1u);
ASSERT_EQ(graph[1u].out_edges().size(), 1u);
ASSERT_EQ(graph[2u].out_edges().size(), 1u);
ASSERT_EQ(graph[3u].out_edges().size(), 1u);
ASSERT_EQ(graph[4u].out_edges().size(), 0u);
ASSERT_EQ(graph[0u].out_edges()[0u], 4u);
ASSERT_EQ(graph[1u].out_edges()[0u], 4u);
ASSERT_EQ(graph[2u].out_edges()[0u], 3u);
ASSERT_EQ(graph[3u].out_edges()[0u], 4u);
for(auto &&vertex: graph) {
typename entt::organizer::function_type *cb = vertex.callback();
ASSERT_NO_THROW(cb(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[1u].in_edges().size(), 1u);
ASSERT_EQ(graph[2u].in_edges().size(), 1u);
ASSERT_EQ(graph[3u].in_edges().size(), 1u);
ASSERT_EQ(graph[1u].in_edges()[0u], 0u);
ASSERT_EQ(graph[2u].in_edges()[0u], 1u);
ASSERT_EQ(graph[3u].in_edges()[0u], 2u);
ASSERT_EQ(graph[0u].out_edges().size(), 1u);
ASSERT_EQ(graph[1u].out_edges().size(), 1u);
ASSERT_EQ(graph[2u].out_edges().size(), 1u);
ASSERT_EQ(graph[3u].out_edges().size(), 0u);
ASSERT_EQ(graph[0u].out_edges()[0u], 1u);
ASSERT_EQ(graph[1u].out_edges()[0u], 2u);
ASSERT_EQ(graph[2u].out_edges()[0u], 3u);
for(auto &&vertex: graph) {
typename entt::organizer::function_type *cb = vertex.callback();
ASSERT_NO_THROW(cb(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[1u].in_edges().size(), 1u);
ASSERT_EQ(graph[2u].in_edges().size(), 1u);
ASSERT_EQ(graph[3u].in_edges().size(), 1u);
ASSERT_EQ(graph[4u].in_edges().size(), 2u);
ASSERT_EQ(graph[5u].in_edges().size(), 1u);
ASSERT_EQ(graph[1u].in_edges()[0u], 0u);
ASSERT_EQ(graph[2u].in_edges()[0u], 1u);
ASSERT_EQ(graph[3u].in_edges()[0u], 1u);
ASSERT_EQ(graph[4u].in_edges()[0u], 2u);
ASSERT_EQ(graph[4u].in_edges()[1u], 3u);
ASSERT_EQ(graph[5u].in_edges()[0u], 4u);
ASSERT_EQ(graph[0u].out_edges().size(), 1u);
ASSERT_EQ(graph[1u].out_edges().size(), 2u);
ASSERT_EQ(graph[2u].out_edges().size(), 1u);
ASSERT_EQ(graph[3u].out_edges().size(), 1u);
ASSERT_EQ(graph[4u].out_edges().size(), 1u);
ASSERT_EQ(graph[5u].out_edges().size(), 0u);
ASSERT_EQ(graph[0u].out_edges()[0u], 1u);
ASSERT_EQ(graph[1u].out_edges()[0u], 2u);
ASSERT_EQ(graph[1u].out_edges()[1u], 3u);
ASSERT_EQ(graph[2u].out_edges()[0u], 4u);
ASSERT_EQ(graph[3u].out_edges()[0u], 4u);
ASSERT_EQ(graph[4u].out_edges()[0u], 5u);
for(auto &&vertex: graph) {
typename entt::organizer::function_type *cb = vertex.callback();
ASSERT_NO_THROW(cb(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[2u].in_edges().size(), 2u);
ASSERT_EQ(graph[2u].in_edges()[0u], 0u);
ASSERT_EQ(graph[2u].in_edges()[1u], 1u);
ASSERT_EQ(graph[0u].out_edges().size(), 1u);
ASSERT_EQ(graph[1u].out_edges().size(), 1u);
ASSERT_EQ(graph[2u].out_edges().size(), 0u);
ASSERT_EQ(graph[0u].out_edges()[0u], 2u);
ASSERT_EQ(graph[1u].out_edges()[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>());
ASSERT_EQ(std::as_const(registry).storage<int>(), nullptr);
ASSERT_EQ(std::as_const(registry).storage<char>(), nullptr);
ASSERT_EQ(std::as_const(registry).storage<double>(), nullptr);
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>());
ASSERT_NE(std::as_const(registry).storage<int>(), nullptr);
ASSERT_NE(std::as_const(registry).storage<char>(), nullptr);
ASSERT_EQ(std::as_const(registry).storage<double>(), nullptr);
}
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();
constexpr auto number_of_elements = 5u;
std::array<const entt::type_info *, number_of_elements> buffer{};
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.data(), 0u), 0u);
ASSERT_EQ(graph[0u].rw_dependency(buffer.data(), 2u), 0u);
ASSERT_EQ(graph[0u].ro_dependency(buffer.data(), 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.data(), 2u), 0u);
ASSERT_EQ(graph[1u].rw_dependency(buffer.data(), 0u), 0u);
ASSERT_EQ(graph[1u].rw_dependency(buffer.data(), 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.data(), 2u), 1u);
ASSERT_EQ(graph[2u].rw_dependency(buffer.data(), 0u), 0u);
ASSERT_EQ(graph[2u].ro_dependency(buffer.data(), 5u), 1u);
ASSERT_EQ(*buffer[0u], entt::type_id<double>());
ASSERT_EQ(graph[2u].rw_dependency(buffer.data(), 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>(2u);
auto graph = organizer.graph();
graph[0u].callback()(graph[0u].data(), registry);
ASSERT_EQ(registry.ctx().get<std::size_t>(), 0u);
}

View File

@@ -0,0 +1,656 @@
#include <array>
#include <cstddef>
#include <iterator>
#include <memory>
#include <type_traits>
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/type_info.hpp>
#include <entt/entity/component.hpp>
#include <entt/entity/mixin.hpp>
#include <entt/entity/registry.hpp>
#include <entt/entity/storage.hpp>
#include <entt/signal/sigh.hpp>
#include "../../common/config.h"
#include "../../common/empty.h"
#include "../../common/entity.h"
#include "../../common/linter.hpp"
#include "../../common/registry.h"
#include "../../common/throwing_allocator.hpp"
template<typename Type, std::size_t Value>
void emplace(Type &storage, const typename Type::registry_type &, const typename Type::entity_type entity) {
if((entity == typename Type::entity_type{Value}) && !storage.contains(entity)) {
storage.emplace(entity);
}
}
template<typename Type>
void remove(Type &storage, const typename Type::registry_type &, const typename Type::entity_type entity) {
storage.remove(entity);
}
template<typename Type>
struct entt::storage_type<Type, test::entity, std::allocator<Type>, std::enable_if_t<!std::is_same_v<Type, test::entity>>> {
using type = entt::basic_sigh_mixin<entt::basic_storage<Type, test::entity>, test::custom_registry<test::entity>>;
};
template<typename Type>
struct ReactiveMixin: testing::Test {
using type = Type;
};
template<typename Type>
using ReactiveMixinDeathTest = ReactiveMixin<Type>;
using ReactiveMixinTypes = ::testing::Types<void, bool>;
TYPED_TEST_SUITE(ReactiveMixin, ReactiveMixinTypes, );
TYPED_TEST_SUITE(ReactiveMixinDeathTest, ReactiveMixinTypes, );
TYPED_TEST(ReactiveMixin, Constructors) {
using value_type = typename TestFixture::type;
using traits_type = entt::component_traits<value_type>;
entt::reactive_mixin<entt::storage<value_type>> pool;
ASSERT_EQ(pool.policy(), entt::deletion_policy{traits_type::in_place_delete});
ASSERT_NO_THROW([[maybe_unused]] auto alloc = pool.get_allocator());
ASSERT_EQ(pool.type(), entt::type_id<value_type>());
pool = entt::reactive_mixin<entt::storage<value_type>>{std::allocator<value_type>{}};
ASSERT_EQ(pool.policy(), entt::deletion_policy{traits_type::in_place_delete});
ASSERT_NO_THROW([[maybe_unused]] auto alloc = pool.get_allocator());
ASSERT_EQ(pool.type(), entt::type_id<value_type>());
}
TYPED_TEST(ReactiveMixin, Move) {
using value_type = typename TestFixture::type;
entt::registry registry;
entt::reactive_mixin<entt::storage<value_type>> pool;
const std::array entity{registry.create(), registry.create()};
pool.bind(registry);
pool.template on_construct<test::empty>().template on_update<test::empty>();
registry.emplace<test::empty>(entity[0u]);
static_assert(std::is_move_constructible_v<decltype(pool)>, "Move constructible type required");
static_assert(std::is_move_assignable_v<decltype(pool)>, "Move assignable type required");
ASSERT_TRUE(pool.contains(entity[0u]));
ASSERT_EQ(pool.type(), entt::type_id<value_type>());
entt::reactive_mixin<entt::storage<value_type>> other{std::move(pool)};
test::is_initialized(pool);
ASSERT_TRUE(pool.empty());
ASSERT_FALSE(other.empty());
ASSERT_EQ(other.type(), entt::type_id<value_type>());
ASSERT_EQ(other.index(entity[0u]), 0u);
ASSERT_EQ(&other.registry(), &registry);
other.clear();
registry.replace<test::empty>(entity[0u]);
ASSERT_FALSE(pool.empty());
ASSERT_TRUE(other.empty());
std::swap(other, pool);
pool = std::move(other);
test::is_initialized(other);
ASSERT_FALSE(pool.empty());
ASSERT_TRUE(other.empty());
ASSERT_EQ(pool.index(entity[0u]), 0u);
ASSERT_EQ(&pool.registry(), &registry);
other = entt::reactive_mixin<entt::storage<value_type>>{};
other.bind(registry);
other.template on_construct<test::empty>();
registry.on_construct<test::empty>().disconnect(&pool);
registry.emplace<test::empty>(entity[1u]);
other = std::move(pool);
test::is_initialized(pool);
ASSERT_FALSE(pool.empty());
ASSERT_FALSE(other.empty());
ASSERT_EQ(other.index(entity[0u]), 0u);
}
TYPED_TEST(ReactiveMixin, Swap) {
using value_type = typename TestFixture::type;
entt::registry registry;
entt::reactive_mixin<entt::storage<value_type>> pool;
entt::reactive_mixin<entt::storage<value_type>> other;
const std::array entity{registry.create(), registry.create()};
registry.emplace<test::empty>(entity[0u]);
pool.bind(registry);
pool.template on_construct<test::empty>();
other.bind(registry);
other.template on_destroy<test::empty>();
registry.emplace<test::empty>(entity[1u]);
registry.erase<test::empty>(entity[0u]);
ASSERT_EQ(pool.size(), 1u);
ASSERT_EQ(other.size(), 1u);
pool.swap(other);
ASSERT_EQ(pool.type(), entt::type_id<value_type>());
ASSERT_EQ(other.type(), entt::type_id<value_type>());
ASSERT_EQ(pool.size(), 1u);
ASSERT_EQ(other.size(), 1u);
ASSERT_EQ(pool.index(entity[0u]), 0u);
ASSERT_EQ(other.index(entity[1u]), 0u);
}
TYPED_TEST(ReactiveMixin, OnConstruct) {
using value_type = typename TestFixture::type;
entt::registry registry;
entt::reactive_mixin<entt::storage<value_type>> pool;
const entt::entity entity{registry.create()};
pool.bind(registry);
registry.emplace<test::empty>(entity);
ASSERT_FALSE(pool.contains(entity));
registry.clear<test::empty>();
pool.template on_construct<test::other_empty>();
registry.emplace<test::empty>(entity);
ASSERT_FALSE(pool.contains(entity));
registry.on_construct<test::other_empty>().disconnect(&pool);
registry.clear<test::empty>();
pool.template on_construct<test::empty>();
registry.emplace<test::empty>(entity);
ASSERT_TRUE(pool.contains(entity));
registry.clear<test::empty>();
ASSERT_TRUE(pool.contains(entity));
registry.emplace<test::empty>(entity);
registry.emplace_or_replace<test::empty>(entity);
ASSERT_TRUE(pool.contains(entity));
registry.destroy(entity);
ASSERT_TRUE(pool.contains(entity));
}
TYPED_TEST(ReactiveMixin, OnConstructCallback) {
using value_type = typename TestFixture::type;
entt::registry registry;
entt::reactive_mixin<entt::storage<value_type>> pool;
const std::array entity{registry.create(), registry.create(entt::entity{3})};
pool.bind(registry);
pool.template on_construct<test::empty, &emplace<entt::reactive_mixin<entt::storage<value_type>>, 3u>>();
registry.emplace<test::empty>(entity[0u]);
ASSERT_TRUE(pool.empty());
registry.emplace<test::empty>(entity[1u]);
ASSERT_EQ(pool.size(), 1u);
ASSERT_TRUE(pool.contains(entity[1u]));
pool.clear();
registry.clear<test::empty>();
ASSERT_TRUE(pool.empty());
registry.insert<test::empty>(entity.begin(), entity.end());
ASSERT_EQ(pool.size(), 1u);
ASSERT_TRUE(pool.contains(entity[1u]));
}
ENTT_DEBUG_TYPED_TEST(ReactiveMixinDeathTest, OnConstruct) {
using value_type = typename TestFixture::type;
entt::reactive_mixin<entt::storage<value_type>> pool;
ASSERT_DEATH(pool.template on_construct<test::empty>(), "");
}
TYPED_TEST(ReactiveMixin, OnUpdate) {
using value_type = typename TestFixture::type;
entt::registry registry;
entt::reactive_mixin<entt::storage<value_type>> pool;
const entt::entity entity{registry.create()};
pool.bind(registry);
registry.emplace<test::empty>(entity);
registry.patch<test::empty>(entity);
ASSERT_FALSE(pool.contains(entity));
pool.template on_update<test::other_empty>();
registry.patch<test::empty>(entity);
ASSERT_FALSE(pool.contains(entity));
registry.on_update<test::other_empty>().disconnect(&pool);
pool.template on_update<test::empty>();
registry.patch<test::empty>(entity);
ASSERT_TRUE(pool.contains(entity));
registry.clear<test::empty>();
ASSERT_TRUE(pool.contains(entity));
registry.emplace<test::empty>(entity);
registry.emplace_or_replace<test::empty>(entity);
ASSERT_TRUE(pool.contains(entity));
registry.destroy(entity);
ASSERT_TRUE(pool.contains(entity));
}
TYPED_TEST(ReactiveMixin, OnUpdateCallback) {
using value_type = typename TestFixture::type;
entt::registry registry;
entt::reactive_mixin<entt::storage<value_type>> pool;
const std::array entity{registry.create(), registry.create(entt::entity{3})};
pool.bind(registry);
pool.template on_update<test::empty, &emplace<entt::reactive_mixin<entt::storage<value_type>>, 3u>>();
registry.insert<test::empty>(entity.begin(), entity.end());
registry.patch<test::empty>(entity[0u]);
ASSERT_TRUE(pool.empty());
registry.patch<test::empty>(entity[1u]);
ASSERT_EQ(pool.size(), 1u);
ASSERT_TRUE(pool.contains(entity[1u]));
pool.clear();
registry.clear<test::empty>();
ASSERT_TRUE(pool.empty());
registry.insert<test::empty>(entity.begin(), entity.end());
registry.patch<test::empty>(entity[0u]);
registry.patch<test::empty>(entity[1u]);
ASSERT_EQ(pool.size(), 1u);
ASSERT_TRUE(pool.contains(entity[1u]));
}
ENTT_DEBUG_TYPED_TEST(ReactiveMixinDeathTest, OnUpdate) {
using value_type = typename TestFixture::type;
entt::reactive_mixin<entt::storage<value_type>> pool;
ASSERT_DEATH(pool.template on_update<test::empty>(), "");
}
TYPED_TEST(ReactiveMixin, OnDestroy) {
using value_type = typename TestFixture::type;
entt::registry registry;
entt::reactive_mixin<entt::storage<value_type>> pool;
const entt::entity entity{registry.create()};
pool.bind(registry);
registry.emplace<test::empty>(entity);
registry.erase<test::empty>(entity);
ASSERT_FALSE(pool.contains(entity));
pool.template on_destroy<test::other_empty>();
registry.emplace<test::empty>(entity);
registry.erase<test::empty>(entity);
ASSERT_FALSE(pool.contains(entity));
registry.on_destroy<test::other_empty>().disconnect(&pool);
pool.template on_destroy<test::empty>();
registry.emplace<test::empty>(entity);
registry.erase<test::empty>(entity);
ASSERT_TRUE(pool.contains(entity));
registry.clear<test::empty>();
ASSERT_TRUE(pool.contains(entity));
registry.emplace<test::empty>(entity);
registry.erase<test::empty>(entity);
ASSERT_TRUE(pool.contains(entity));
registry.destroy(entity);
ASSERT_TRUE(pool.contains(entity));
}
TYPED_TEST(ReactiveMixin, OnDestroyCallback) {
using value_type = typename TestFixture::type;
entt::registry registry;
entt::reactive_mixin<entt::storage<value_type>> pool;
const std::array entity{registry.create(), registry.create(entt::entity{3})};
pool.bind(registry);
pool.template on_destroy<test::empty, &emplace<entt::reactive_mixin<entt::storage<value_type>>, 3u>>();
registry.insert<test::empty>(entity.begin(), entity.end());
registry.erase<test::empty>(entity[0u]);
ASSERT_TRUE(pool.empty());
registry.erase<test::empty>(entity[1u]);
ASSERT_EQ(pool.size(), 1u);
ASSERT_TRUE(pool.contains(entity[1u]));
pool.clear();
ASSERT_TRUE(pool.empty());
registry.insert<test::empty>(entity.begin(), entity.end());
registry.erase<test::empty>(entity.begin(), entity.end());
ASSERT_EQ(pool.size(), 1u);
ASSERT_TRUE(pool.contains(entity[1u]));
}
ENTT_DEBUG_TYPED_TEST(ReactiveMixinDeathTest, OnDestroy) {
using value_type = typename TestFixture::type;
entt::reactive_mixin<entt::storage<value_type>> pool;
ASSERT_DEATH(pool.template on_destroy<test::empty>(), "");
}
TYPED_TEST(ReactiveMixin, EntityLifecycle) {
using value_type = typename TestFixture::type;
entt::registry registry;
entt::reactive_mixin<entt::storage<value_type>> pool;
const entt::entity entity{registry.create()};
pool.bind(registry);
pool.template on_construct<test::empty>().template on_destroy<entt::entity, &remove<decltype(pool)>>();
ASSERT_FALSE(pool.contains(entity));
registry.emplace<test::empty>(entity);
ASSERT_TRUE(pool.contains(entity));
registry.erase<test::empty>(entity);
ASSERT_TRUE(pool.contains(entity));
registry.emplace<test::empty>(entity);
registry.destroy(entity);
ASSERT_FALSE(pool.contains(entity));
}
TYPED_TEST(ReactiveMixin, ManagedStorage) {
entt::registry registry;
entt::storage_for_t<entt::reactive> &pool = registry.storage<entt::reactive>();
const entt::entity entity{registry.create()};
pool.template on_construct<test::empty>();
registry.emplace<test::empty>(entity);
ASSERT_TRUE(pool.contains(entity));
registry.erase<test::empty>(entity);
ASSERT_TRUE(pool.contains(entity));
registry.emplace<test::empty>(entity);
registry.destroy(entity);
ASSERT_FALSE(pool.contains(entity));
}
TYPED_TEST(ReactiveMixin, Registry) {
using value_type = typename TestFixture::type;
entt::registry registry;
entt::reactive_mixin<entt::storage<value_type>> pool;
ASSERT_FALSE(pool);
pool.bind(registry);
ASSERT_TRUE(pool);
ASSERT_EQ(&pool.registry(), &registry);
ASSERT_EQ(&std::as_const(pool).registry(), &registry);
}
ENTT_DEBUG_TYPED_TEST(ReactiveMixinDeathTest, Registry) {
using value_type = typename TestFixture::type;
entt::reactive_mixin<entt::storage<value_type>> pool;
ASSERT_DEATH([[maybe_unused]] auto &registry = pool.registry(), "");
ASSERT_DEATH([[maybe_unused]] const auto &registry = std::as_const(pool).registry(), "");
}
TYPED_TEST(ReactiveMixin, CustomRegistry) {
using value_type = typename TestFixture::type;
using registry_type = test::custom_registry<test::entity>;
registry_type registry;
entt::basic_reactive_mixin<entt::basic_storage<value_type, test::entity>, registry_type> pool;
const std::array entity{registry.create(), registry.create()};
ASSERT_FALSE(pool);
pool.bind(registry);
ASSERT_TRUE(pool);
pool.template on_construct<test::empty>();
registry.insert<test::empty>(entity.begin(), entity.end());
ASSERT_EQ(pool.size(), 2u);
ASSERT_TRUE(pool.contains(entity[0u]));
ASSERT_TRUE(pool.contains(entity[1u]));
}
ENTT_DEBUG_TYPED_TEST(ReactiveMixinDeathTest, CustomRegistry) {
using value_type = typename TestFixture::type;
using registry_type = test::custom_registry<test::entity>;
entt::basic_reactive_mixin<entt::basic_storage<value_type, test::entity>, registry_type> pool;
ASSERT_DEATH([[maybe_unused]] auto &registry = pool.registry(), "");
ASSERT_DEATH([[maybe_unused]] const auto &registry = std::as_const(pool).registry(), "");
}
TYPED_TEST(ReactiveMixin, View) {
using value_type = typename TestFixture::type;
entt::registry registry;
entt::reactive_mixin<entt::storage<value_type>> pool;
const std::array entity{registry.create(), registry.create()};
pool.bind(registry);
pool.template on_construct<test::empty>();
registry.insert<test::empty>(entity.begin(), entity.end());
registry.insert<double>(entity.begin(), entity.end());
registry.emplace<int>(entity[1u], 1);
ASSERT_EQ(pool.size(), 2u);
ASSERT_TRUE(pool.contains(entity[0u]));
ASSERT_TRUE(pool.contains(entity[1u]));
const auto view = pool.view();
const auto cview = std::as_const(pool).view();
ASSERT_EQ(view.size(), 2u);
ASSERT_EQ(view.front(), entity[1u]);
ASSERT_EQ(view.back(), entity[0u]);
ASSERT_EQ(cview.size(), view.size());
for(const auto entt: cview) {
ASSERT_TRUE(view.contains(entt));
}
const auto filtered = pool.template view<double>(entt::exclude<int>);
const auto cfiltered = std::as_const(pool).template view<double>(entt::exclude<int>);
ASSERT_EQ(filtered.size_hint(), 2u);
ASSERT_EQ(std::distance(filtered.begin(), filtered.end()), 1);
ASSERT_TRUE(filtered.contains(entity[0u]));
ASSERT_FALSE(filtered.contains(entity[1u]));
ASSERT_NE(std::distance(cfiltered.begin(), cfiltered.end()), 0);
for(const auto entt: cfiltered) {
ASSERT_TRUE(filtered.contains(entt));
}
}
ENTT_DEBUG_TYPED_TEST(ReactiveMixinDeathTest, View) {
using value_type = typename TestFixture::type;
entt::reactive_mixin<entt::storage<value_type>> pool;
ASSERT_DEATH([[maybe_unused]] const auto view = pool.view(), "");
ASSERT_DEATH([[maybe_unused]] const auto cview = std::as_const(pool).view(), "");
}
TYPED_TEST(ReactiveMixin, AutoDisconnection) {
using value_type = typename TestFixture::type;
entt::registry registry;
entt::reactive_mixin<entt::storage<value_type>> pool;
const std::array entity{registry.create(), registry.create(), registry.create()};
ASSERT_TRUE(pool.empty());
ASSERT_TRUE(registry.on_construct<test::empty>().empty());
ASSERT_TRUE(registry.on_update<test::empty>().empty());
ASSERT_TRUE(registry.on_destroy<test::empty>().empty());
pool.bind(registry);
pool.template on_construct<test::empty>();
pool.template on_update<test::empty>();
pool.template on_destroy<test::empty>();
registry.emplace<test::empty>(entity[0u]);
ASSERT_FALSE(pool.empty());
ASSERT_FALSE(registry.on_construct<test::empty>().empty());
ASSERT_FALSE(registry.on_update<test::empty>().empty());
ASSERT_FALSE(registry.on_destroy<test::empty>().empty());
pool.reset();
ASSERT_FALSE(pool.empty());
ASSERT_TRUE(registry.on_construct<test::empty>().empty());
ASSERT_TRUE(registry.on_update<test::empty>().empty());
ASSERT_TRUE(registry.on_destroy<test::empty>().empty());
}
TYPED_TEST(ReactiveMixin, CustomAllocator) {
using value_type = typename TestFixture::type;
using storage_type = entt::reactive_mixin<entt::basic_storage<value_type, entt::entity, test::throwing_allocator<value_type>>>;
using registry_type = typename storage_type::registry_type;
const test::throwing_allocator<entt::entity> allocator{};
storage_type pool{allocator};
registry_type registry;
const std::array entity{registry.create(), registry.create()};
pool.bind(registry);
pool.template on_construct<test::empty>();
pool.reserve(1u);
ASSERT_NE(pool.capacity(), 0u);
registry.template emplace<test::empty>(entity[0u]);
registry.template emplace<test::empty>(entity[1u]);
decltype(pool) other{std::move(pool), allocator};
test::is_initialized(pool);
ASSERT_TRUE(pool.empty());
ASSERT_FALSE(other.empty());
ASSERT_NE(other.capacity(), 0u);
ASSERT_EQ(other.size(), 2u);
pool = std::move(other);
test::is_initialized(other);
ASSERT_FALSE(pool.empty());
ASSERT_TRUE(other.empty());
ASSERT_NE(pool.capacity(), 0u);
ASSERT_EQ(pool.size(), 2u);
other = {};
pool.swap(other);
pool = std::move(other);
test::is_initialized(other);
ASSERT_FALSE(pool.empty());
ASSERT_TRUE(other.empty());
ASSERT_NE(pool.capacity(), 0u);
ASSERT_EQ(pool.size(), 2u);
pool.clear();
ASSERT_NE(pool.capacity(), 0u);
ASSERT_EQ(pool.size(), 0u);
}
TYPED_TEST(ReactiveMixin, ThrowingAllocator) {
using value_type = typename TestFixture::type;
using storage_type = entt::reactive_mixin<entt::basic_storage<value_type, entt::entity, test::throwing_allocator<value_type>>>;
using registry_type = typename storage_type::registry_type;
storage_type pool{};
registry_type registry;
const std::array entity{registry.create(), registry.create()};
pool.bind(registry);
pool.template on_construct<test::empty>();
pool.get_allocator().template throw_counter<entt::entity>(0u);
ASSERT_THROW(pool.reserve(1u), test::throwing_allocator_exception);
ASSERT_EQ(pool.capacity(), 0u);
pool.get_allocator().template throw_counter<entt::entity>(1u);
ASSERT_THROW(registry.template emplace<test::empty>(entity[0u]), test::throwing_allocator_exception);
ASSERT_TRUE(registry.template all_of<test::empty>(entity[0u]));
ASSERT_FALSE(pool.contains(entity[0u]));
registry.template clear<test::empty>();
pool.get_allocator().template throw_counter<entt::entity>(1u);
ASSERT_THROW(registry.template insert<test::empty>(entity.begin(), entity.end()), test::throwing_allocator_exception);
ASSERT_TRUE(registry.template all_of<test::empty>(entity[0u]));
ASSERT_TRUE(registry.template all_of<test::empty>(entity[1u]));
ASSERT_TRUE(pool.contains(entity[0u]));
ASSERT_FALSE(pool.contains(entity[1u]));
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,420 @@
#include <algorithm>
#include <array>
#include <memory>
#include <tuple>
#include <utility>
#include <gtest/gtest.h>
#include <entt/entity/entity.hpp>
#include <entt/entity/runtime_view.hpp>
#include <entt/entity/storage.hpp>
#include "../../common/linter.hpp"
#include "../../common/pointer_stable.h"
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;
std::tuple<entt::storage<int>, entt::storage<char>> storage{};
const std::array entity{entt::entity{1}, entt::entity{3}};
runtime_view_type view{};
ASSERT_FALSE(view);
ASSERT_EQ(view.size_hint(), 0u);
ASSERT_EQ(view.begin(), view.end());
ASSERT_FALSE(view.contains(entity[0u]));
ASSERT_FALSE(view.contains(entity[1u]));
view.iterate(std::get<0>(storage)).iterate(std::get<1>(storage));
ASSERT_TRUE(view);
ASSERT_EQ(view.size_hint(), 0u);
std::get<1>(storage).emplace(entity[0u]);
std::get<0>(storage).emplace(entity[1u]);
ASSERT_NE(view.size_hint(), 0u);
std::get<1>(storage).emplace(entity[1u]);
ASSERT_EQ(view.size_hint(), 1u);
auto it = view.begin();
ASSERT_EQ(*it, entity[1u]);
ASSERT_EQ(++it, (view.end()));
ASSERT_NO_THROW((view.begin()++));
ASSERT_NO_THROW((++view.begin()));
ASSERT_NE(view.begin(), view.end());
ASSERT_EQ(view.size_hint(), 1u);
std::get<1>(storage).get(entity[0u]) = '1';
std::get<1>(storage).get(entity[1u]) = '2';
std::get<0>(storage).get(entity[1u]) = 3;
for(auto entt: view) {
ASSERT_EQ(std::get<0>(storage).get(entt), 3);
ASSERT_EQ(std::get<1>(storage).get(entt), '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::storage<int> storage{};
const entt::entity entity{0};
runtime_view_type view{};
ASSERT_FALSE(view);
storage.emplace(entity);
view = runtime_view_type{std::allocator<int>{}};
view.iterate(storage);
ASSERT_TRUE(view);
ASSERT_TRUE(view.contains(entity));
runtime_view_type temp{view, view.get_allocator()};
const runtime_view_type other{std::move(temp), view.get_allocator()};
test::is_initialized(temp);
ASSERT_FALSE(temp);
ASSERT_TRUE(other);
ASSERT_TRUE(view.contains(entity));
ASSERT_TRUE(other.contains(entity));
}
TYPED_TEST(RuntimeView, Copy) {
using runtime_view_type = typename TestFixture::type;
std::tuple<entt::storage<int>, entt::storage<char>> storage{};
const entt::entity entity{0};
runtime_view_type view{};
ASSERT_FALSE(view);
std::get<0>(storage).emplace(entity);
std::get<1>(storage).emplace(entity);
view.iterate(std::get<0>(storage));
runtime_view_type other{view};
ASSERT_TRUE(view);
ASSERT_TRUE(other);
ASSERT_TRUE(view.contains(entity));
ASSERT_TRUE(other.contains(entity));
other.iterate(std::get<0>(storage)).exclude(std::get<1>(storage));
ASSERT_TRUE(view.contains(entity));
ASSERT_FALSE(other.contains(entity));
other = view;
ASSERT_TRUE(view);
ASSERT_TRUE(other);
ASSERT_TRUE(view.contains(entity));
ASSERT_TRUE(other.contains(entity));
}
TYPED_TEST(RuntimeView, Move) {
using runtime_view_type = typename TestFixture::type;
std::tuple<entt::storage<int>, entt::storage<char>> storage{};
const entt::entity entity{0};
runtime_view_type view{};
ASSERT_FALSE(view);
std::get<0>(storage).emplace(entity);
std::get<1>(storage).emplace(entity);
view.iterate(std::get<0>(storage));
runtime_view_type other{std::move(view)};
test::is_initialized(view);
ASSERT_FALSE(view);
ASSERT_TRUE(other);
ASSERT_TRUE(other.contains(entity));
view = other;
other.iterate(std::get<0>(storage)).exclude(std::get<1>(storage));
ASSERT_TRUE(view);
ASSERT_TRUE(other);
ASSERT_TRUE(view.contains(entity));
ASSERT_FALSE(other.contains(entity));
other = std::move(view);
test::is_initialized(view);
ASSERT_FALSE(view);
ASSERT_TRUE(other);
ASSERT_TRUE(other.contains(entity));
}
TYPED_TEST(RuntimeView, Swap) {
using runtime_view_type = typename TestFixture::type;
entt::storage<int> storage{};
const entt::entity entity{0};
runtime_view_type view{};
runtime_view_type other{};
ASSERT_FALSE(view);
ASSERT_FALSE(other);
storage.emplace(entity);
view.iterate(storage);
ASSERT_TRUE(view);
ASSERT_FALSE(other);
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_FALSE(view);
ASSERT_TRUE(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::storage<int> storage{};
const entt::entity entity{0};
runtime_view_type view{};
storage.emplace(entity);
view.iterate(storage);
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::storage<int> storage{};
const std::array entity{entt::entity{1}, entt::entity{3}};
runtime_view_type view{};
storage.emplace(entity[0u]);
storage.emplace(entity[1u]);
storage.erase(entity[0u]);
view.iterate(storage);
ASSERT_FALSE(view.contains(entity[0u]));
ASSERT_TRUE(view.contains(entity[1u]));
}
TYPED_TEST(RuntimeView, Empty) {
using runtime_view_type = typename TestFixture::type;
entt::storage<int> storage{};
const entt::entity entity{0};
runtime_view_type view{};
view.iterate(storage);
ASSERT_FALSE(view.contains(entity));
ASSERT_EQ(view.begin(), view.end());
ASSERT_EQ((std::find(view.begin(), view.end(), entity)), view.end());
storage.emplace(entity);
ASSERT_TRUE(view.contains(entity));
ASSERT_NE(view.begin(), view.end());
ASSERT_NE((std::find(view.begin(), view.end(), entity)), view.end());
}
TYPED_TEST(RuntimeView, Each) {
using runtime_view_type = typename TestFixture::type;
std::tuple<entt::storage<int>, entt::storage<char>> storage{};
const std::array entity{entt::entity{1}, entt::entity{3}};
runtime_view_type view{};
std::get<0>(storage).emplace(entity[0u]);
std::get<1>(storage).emplace(entity[0u]);
std::get<1>(storage).emplace(entity[1u]);
view.iterate(std::get<0>(storage)).iterate(std::get<1>(storage));
view.each([&](const auto entt) {
ASSERT_EQ(entt, entity[0u]);
});
}
TYPED_TEST(RuntimeView, EachWithHoles) {
using runtime_view_type = typename TestFixture::type;
std::tuple<entt::storage<int>, entt::storage<char>> storage{};
const std::array entity{entt::entity{0}, entt::entity{1}, entt::entity{3}};
runtime_view_type view{};
std::get<1>(storage).emplace(entity[0u], '0');
std::get<1>(storage).emplace(entity[1u], '1');
std::get<0>(storage).emplace(entity[0u], 0);
std::get<0>(storage).emplace(entity[2u], 2);
view.iterate(std::get<0>(storage)).iterate(std::get<1>(storage));
view.each([&](auto entt) {
ASSERT_EQ(entt, entity[0u]);
});
}
TYPED_TEST(RuntimeView, ExcludedComponents) {
using runtime_view_type = typename TestFixture::type;
std::tuple<entt::storage<int>, entt::storage<char>> storage{};
const std::array entity{entt::entity{1}, entt::entity{3}};
runtime_view_type view{};
std::get<0>(storage).emplace(entity[0u]);
std::get<0>(storage).emplace(entity[1u]);
std::get<1>(storage).emplace(entity[1u]);
view.iterate(std::get<0>(storage)).exclude(std::get<1>(storage));
ASSERT_TRUE(view.contains(entity[0u]));
ASSERT_FALSE(view.contains(entity[1u]));
view.each([&](auto entt) {
ASSERT_EQ(entt, entity[0u]);
});
}
TYPED_TEST(RuntimeView, StableType) {
using runtime_view_type = typename TestFixture::type;
std::tuple<entt::storage<int>, entt::storage<test::pointer_stable>> storage{};
const std::array entity{entt::entity{0}, entt::entity{1}, entt::entity{3}};
runtime_view_type view{};
std::get<0>(storage).emplace(entity[0u]);
std::get<0>(storage).emplace(entity[1u]);
std::get<0>(storage).emplace(entity[2u]);
std::get<1>(storage).emplace(entity[0u]);
std::get<1>(storage).emplace(entity[1u]);
std::get<1>(storage).remove(entity[1u]);
view.iterate(std::get<0>(storage)).iterate(std::get<1>(storage));
ASSERT_EQ(view.size_hint(), 2u);
ASSERT_TRUE(view.contains(entity[0u]));
ASSERT_FALSE(view.contains(entity[1u]));
ASSERT_EQ(*view.begin(), entity[0u]);
ASSERT_EQ(++view.begin(), view.end());
view.each([&](const auto entt) {
ASSERT_EQ(entt, entity[0u]);
});
for(auto entt: view) {
testing::StaticAssertTypeEq<decltype(entt), entt::entity>();
ASSERT_EQ(entt, entity[0u]);
}
std::get<1>(storage).compact();
ASSERT_EQ(view.size_hint(), 1u);
}
TYPED_TEST(RuntimeView, StableTypeWithExcludedComponent) {
using runtime_view_type = typename TestFixture::type;
constexpr entt::entity tombstone = entt::tombstone;
std::tuple<entt::storage<int>, entt::storage<test::pointer_stable>> storage{};
const std::array entity{entt::entity{1}, entt::entity{3}};
runtime_view_type view{};
std::get<1>(storage).emplace(entity[0u], 0);
std::get<1>(storage).emplace(entity[1u], 1);
std::get<0>(storage).emplace(entity[0u]);
view.iterate(std::get<1>(storage)).exclude(std::get<0>(storage));
ASSERT_EQ(view.size_hint(), 2u);
ASSERT_FALSE(view.contains(entity[0u]));
ASSERT_TRUE(view.contains(entity[1u]));
std::get<0>(storage).erase(entity[0u]);
std::get<1>(storage).erase(entity[0u]);
ASSERT_EQ(view.size_hint(), 2u);
ASSERT_FALSE(view.contains(entity[0u]));
ASSERT_TRUE(view.contains(entity[1u]));
for(auto entt: view) {
ASSERT_NE(entt, tombstone);
ASSERT_EQ(entt, entity[1u]);
}
view.each([&](const auto entt) {
ASSERT_NE(entt, tombstone);
ASSERT_EQ(entt, entity[1u]);
});
}

View File

@@ -0,0 +1,720 @@
#include <array>
#include <cstddef>
#include <memory>
#include <type_traits>
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/type_info.hpp>
#include <entt/entity/component.hpp>
#include <entt/entity/entity.hpp>
#include <entt/entity/mixin.hpp>
#include <entt/entity/registry.hpp>
#include <entt/entity/storage.hpp>
#include <entt/signal/sigh.hpp>
#include "../../common/config.h"
#include "../../common/entity.h"
#include "../../common/linter.hpp"
#include "../../common/non_default_constructible.h"
#include "../../common/pointer_stable.h"
#include "../../common/registry.h"
#include "../../common/throwing_allocator.hpp"
#include "../../common/throwing_type.hpp"
struct auto_signal final {
auto_signal(bool &cflag, bool &uflag, bool &dflag)
: created{&cflag},
updated{&uflag},
destroyed{&dflag} {}
static void on_construct(entt::registry &registry, const entt::entity entt) {
*registry.get<auto_signal>(entt).created = true;
}
static void on_update(entt::registry &registry, const entt::entity entt) {
*registry.get<auto_signal>(entt).updated = true;
}
static void on_destroy(entt::registry &registry, const entt::entity entt) {
*registry.get<auto_signal>(entt).destroyed = true;
}
private:
bool *created{};
bool *updated{};
bool *destroyed{};
};
template<typename Registry>
void listener(std::size_t &counter, Registry &, typename Registry::entity_type) {
++counter;
}
template<typename Type>
struct SighMixin: testing::Test {
using type = Type;
};
template<typename Type>
using SighMixinDeathTest = SighMixin<Type>;
using SighMixinTypes = ::testing::Types<int, test::pointer_stable>;
TYPED_TEST_SUITE(SighMixin, SighMixinTypes, );
TYPED_TEST_SUITE(SighMixinDeathTest, SighMixinTypes, );
TYPED_TEST(SighMixin, Functionalities) {
using value_type = typename TestFixture::type;
using traits_type = entt::component_traits<value_type>;
entt::registry registry;
auto &pool = registry.storage<value_type>();
const std::array entity{registry.create(), registry.create()};
testing::StaticAssertTypeEq<decltype(pool), entt::sigh_mixin<entt::storage<value_type>> &>();
std::size_t on_construct{};
std::size_t on_destroy{};
ASSERT_EQ(pool.size(), 0u);
pool.insert(entity.begin(), entity.begin() + 1u);
pool.erase(entity[0u]);
ASSERT_EQ(pool.size(), traits_type::in_place_delete);
ASSERT_EQ(on_construct, 0u);
ASSERT_EQ(on_destroy, 0u);
pool.on_construct().template connect<&listener<entt::registry>>(on_construct);
pool.on_destroy().template connect<&listener<entt::registry>>(on_destroy);
ASSERT_NE(pool.push(entity[0u]), pool.entt::sparse_set::end());
pool.emplace(entity[1u]);
ASSERT_EQ(on_construct, 2u);
ASSERT_EQ(on_destroy, 0u);
ASSERT_EQ(pool.size(), 2u);
ASSERT_EQ(pool.get(entity[0u]), value_type{0});
ASSERT_EQ(pool.get(entity[1u]), value_type{0});
pool.erase(entity.begin(), entity.end());
ASSERT_EQ(on_construct, 2u);
ASSERT_EQ(on_destroy, 2u);
ASSERT_EQ(pool.size(), 2u * traits_type::in_place_delete);
ASSERT_NE(pool.push(entity.begin(), entity.end()), pool.entt::sparse_set::end());
ASSERT_EQ(pool.get(entity[0u]), value_type{0});
ASSERT_EQ(pool.get(entity[1u]), value_type{0});
ASSERT_EQ(pool.size(), traits_type::in_place_delete ? 4u : 2u);
pool.erase(entity[1u]);
ASSERT_EQ(on_construct, 4u);
ASSERT_EQ(on_destroy, 3u);
ASSERT_EQ(pool.size(), traits_type::in_place_delete ? 4u : 1u);
pool.erase(entity[0u]);
ASSERT_EQ(on_construct, 4u);
ASSERT_EQ(on_destroy, 4u);
ASSERT_EQ(pool.size(), traits_type::in_place_delete ? 4u : 0u);
pool.insert(entity.begin(), entity.end(), value_type{3});
ASSERT_EQ(on_construct, 6u);
ASSERT_EQ(on_destroy, 4u);
ASSERT_EQ(pool.size(), traits_type::in_place_delete ? 6u : 2u);
ASSERT_EQ(pool.get(entity[0u]), value_type{3});
ASSERT_EQ(pool.get(entity[1u]), value_type{3});
pool.clear();
ASSERT_EQ(on_construct, 6u);
ASSERT_EQ(on_destroy, 6u);
ASSERT_EQ(pool.size(), 0u);
}
TYPED_TEST(SighMixin, InsertWeakRange) {
using value_type = typename TestFixture::type;
entt::registry registry;
auto &pool = registry.storage<value_type>();
const auto view = registry.view<entt::entity>(entt::exclude<value_type>);
[[maybe_unused]] const std::array entity{registry.create(), registry.create()};
std::size_t on_construct{};
ASSERT_EQ(on_construct, 0u);
pool.on_construct().template connect<&listener<entt::registry>>(on_construct);
pool.insert(view.begin(), view.end());
ASSERT_EQ(on_construct, 2u);
}
TEST(SighMixin, NonDefaultConstructibleType) {
entt::registry registry;
auto &pool = registry.storage<test::non_default_constructible>();
const std::array entity{registry.create(), registry.create()};
testing::StaticAssertTypeEq<decltype(pool), entt::sigh_mixin<entt::storage<test::non_default_constructible>> &>();
std::size_t on_construct{};
std::size_t on_destroy{};
ASSERT_EQ(pool.size(), 0u);
pool.insert(entity.begin(), entity.begin() + 1u, 0);
pool.erase(entity[0u]);
ASSERT_EQ(pool.size(), 0u);
ASSERT_EQ(on_construct, 0u);
ASSERT_EQ(on_destroy, 0u);
pool.on_construct().connect<&listener<entt::registry>>(on_construct);
pool.on_destroy().connect<&listener<entt::registry>>(on_destroy);
ASSERT_EQ(pool.push(entity[0u]), pool.entt::sparse_set::end());
pool.emplace(entity[1u], 3);
ASSERT_EQ(on_construct, 1u);
ASSERT_EQ(on_destroy, 0u);
ASSERT_EQ(pool.size(), 1u);
ASSERT_FALSE(pool.contains(entity[0u]));
ASSERT_EQ(pool.get(entity[1u]).value, 3);
pool.erase(entity[1u]);
ASSERT_EQ(on_construct, 1u);
ASSERT_EQ(on_destroy, 1u);
ASSERT_EQ(pool.size(), 0u);
ASSERT_EQ(pool.push(entity.begin(), entity.end()), pool.entt::sparse_set::end());
ASSERT_FALSE(pool.contains(entity[0u]));
ASSERT_FALSE(pool.contains(entity[1u]));
ASSERT_EQ(pool.size(), 0u);
pool.insert(entity.begin(), entity.end(), 3);
ASSERT_EQ(on_construct, 3u);
ASSERT_EQ(on_destroy, 1u);
ASSERT_EQ(pool.size(), 2u);
ASSERT_EQ(pool.get(entity[0u]).value, 3);
ASSERT_EQ(pool.get(entity[1u]).value, 3);
pool.erase(entity.begin(), entity.end());
ASSERT_EQ(on_construct, 3u);
ASSERT_EQ(on_destroy, 3u);
ASSERT_EQ(pool.size(), 0u);
}
TEST(SighMixin, VoidType) {
entt::registry registry;
auto &pool = registry.storage<void>();
const auto entity = registry.create();
testing::StaticAssertTypeEq<decltype(pool), entt::sigh_mixin<entt::storage<void>> &>();
std::size_t on_construct{};
std::size_t on_destroy{};
pool.on_construct().connect<&listener<entt::registry>>(on_construct);
pool.on_destroy().connect<&listener<entt::registry>>(on_destroy);
pool.emplace(entity);
ASSERT_EQ(pool.type(), entt::type_id<void>());
ASSERT_TRUE(pool.contains(entity));
entt::sigh_mixin<entt::storage<void>> other{std::move(pool)};
test::is_initialized(pool);
ASSERT_TRUE(pool.empty());
ASSERT_TRUE(other.contains(entity));
pool = std::move(other);
test::is_initialized(other);
ASSERT_TRUE(pool.contains(entity));
ASSERT_TRUE(other.empty());
pool.clear();
ASSERT_EQ(on_construct, 1u);
ASSERT_EQ(on_destroy, 1u);
}
TEST(SighMixin, StorageEntity) {
using traits_type = entt::entt_traits<entt::entity>;
entt::registry registry;
auto &pool = registry.storage<entt::entity>();
testing::StaticAssertTypeEq<decltype(pool), entt::sigh_mixin<entt::storage<entt::entity>> &>();
std::size_t on_construct{};
std::size_t on_destroy{};
pool.on_construct().connect<&listener<entt::registry>>(on_construct);
pool.on_destroy().connect<&listener<entt::registry>>(on_destroy);
pool.push(entt::entity{1});
ASSERT_EQ(on_construct, 1u);
ASSERT_EQ(on_destroy, 0u);
ASSERT_EQ(pool.size(), 1u);
ASSERT_EQ(pool.free_list(), 1u);
pool.erase(entt::entity{1});
ASSERT_EQ(on_construct, 1u);
ASSERT_EQ(on_destroy, 1u);
ASSERT_EQ(pool.size(), 1u);
ASSERT_EQ(pool.free_list(), 0u);
pool.push(traits_type::construct(0, 2));
pool.push(traits_type::construct(2, 1));
ASSERT_TRUE(pool.contains(traits_type::construct(0, 2)));
ASSERT_TRUE(pool.contains(traits_type::construct(1, 1)));
ASSERT_TRUE(pool.contains(traits_type::construct(2, 1)));
ASSERT_EQ(on_construct, 3u);
ASSERT_EQ(on_destroy, 1u);
ASSERT_EQ(pool.size(), 3u);
ASSERT_EQ(pool.free_list(), 2u);
pool.clear();
ASSERT_EQ(pool.size(), 0u);
ASSERT_EQ(pool.free_list(), 0u);
ASSERT_EQ(on_construct, 3u);
ASSERT_EQ(on_destroy, 3u);
pool.generate();
pool.generate(entt::entity{0});
std::array<entt::entity, 1u> entity{};
pool.generate(entity.begin(), entity.end());
ASSERT_EQ(on_construct, 6u);
ASSERT_EQ(on_destroy, 3u);
ASSERT_EQ(pool.size(), 3u);
ASSERT_EQ(pool.free_list(), 3u);
pool.clear();
ASSERT_EQ(pool.size(), 0u);
ASSERT_EQ(pool.free_list(), 0u);
}
TYPED_TEST(SighMixin, Move) {
using value_type = typename TestFixture::type;
entt::sigh_mixin<entt::storage<value_type>> pool;
entt::registry registry;
std::size_t on_construct{};
std::size_t on_destroy{};
pool.bind(registry);
pool.on_construct().template connect<&listener<entt::registry>>(on_construct);
pool.on_destroy().template connect<&listener<entt::registry>>(on_destroy);
pool.emplace(entt::entity{3}, 3);
static_assert(std::is_move_constructible_v<decltype(pool)>, "Move constructible type required");
static_assert(std::is_move_assignable_v<decltype(pool)>, "Move assignable type required");
ASSERT_EQ(pool.type(), entt::type_id<value_type>());
entt::sigh_mixin<entt::storage<value_type>> other{std::move(pool)};
test::is_initialized(pool);
ASSERT_TRUE(pool.empty());
ASSERT_FALSE(other.empty());
ASSERT_EQ(other.type(), entt::type_id<value_type>());
ASSERT_EQ(other.index(entt::entity{3}), 0u);
ASSERT_EQ(other.get(entt::entity{3}), value_type{3});
pool = std::move(other);
test::is_initialized(other);
ASSERT_FALSE(pool.empty());
ASSERT_TRUE(other.empty());
ASSERT_EQ(pool.index(entt::entity{3}), 0u);
ASSERT_EQ(pool.get(entt::entity{3}), value_type{3});
other = entt::sigh_mixin<entt::storage<value_type>>{};
other.bind(registry);
other.emplace(entt::entity{1}, 1);
other = std::move(pool);
test::is_initialized(pool);
ASSERT_FALSE(pool.empty());
ASSERT_FALSE(other.empty());
ASSERT_EQ(other.index(entt::entity{3}), 0u);
ASSERT_EQ(other.get(entt::entity{3}), value_type{3});
other.clear();
ASSERT_EQ(on_construct, 1u);
ASSERT_EQ(on_destroy, 1u);
}
TYPED_TEST(SighMixin, Swap) {
using value_type = typename TestFixture::type;
using traits_type = entt::component_traits<value_type>;
entt::sigh_mixin<entt::storage<value_type>> pool;
entt::sigh_mixin<entt::storage<value_type>> other;
entt::registry registry;
std::size_t on_construct{};
std::size_t on_destroy{};
pool.bind(registry);
pool.on_construct().template connect<&listener<entt::registry>>(on_construct);
pool.on_destroy().template connect<&listener<entt::registry>>(on_destroy);
other.bind(registry);
other.on_construct().template connect<&listener<entt::registry>>(on_construct);
other.on_destroy().template connect<&listener<entt::registry>>(on_destroy);
pool.emplace(entt::entity{4}, 1);
other.emplace(entt::entity{2}, 2);
other.emplace(entt::entity{1}, 3);
other.erase(entt::entity{2});
ASSERT_EQ(pool.size(), 1u);
ASSERT_EQ(other.size(), 1u + traits_type::in_place_delete);
pool.swap(other);
ASSERT_EQ(pool.type(), entt::type_id<value_type>());
ASSERT_EQ(other.type(), entt::type_id<value_type>());
ASSERT_EQ(pool.size(), 1u + traits_type::in_place_delete);
ASSERT_EQ(other.size(), 1u);
ASSERT_EQ(pool.index(entt::entity{1}), traits_type::in_place_delete);
ASSERT_EQ(other.index(entt::entity{4}), 0u);
ASSERT_EQ(pool.get(entt::entity{1}), value_type{3});
ASSERT_EQ(other.get(entt::entity{4}), value_type{1});
pool.clear();
other.clear();
ASSERT_EQ(on_construct, 3u);
ASSERT_EQ(on_destroy, 3u);
}
TEST(SighMixin, AutoSignal) {
entt::registry registry;
const auto entity = registry.create();
bool created{};
bool updated{};
bool destroyed{};
registry.emplace<auto_signal>(entity, created, updated, destroyed);
registry.replace<auto_signal>(entity, created, updated, destroyed);
registry.erase<auto_signal>(entity);
ASSERT_TRUE(created);
ASSERT_TRUE(updated);
ASSERT_TRUE(destroyed);
ASSERT_TRUE(registry.storage<auto_signal>().empty());
ASSERT_TRUE(registry.valid(entity));
created = updated = destroyed = false;
registry.emplace<auto_signal>(entity, created, updated, destroyed);
registry.replace<auto_signal>(entity, created, updated, destroyed);
registry.destroy(entity);
ASSERT_TRUE(created);
ASSERT_TRUE(updated);
ASSERT_TRUE(destroyed);
ASSERT_TRUE(registry.storage<auto_signal>().empty());
ASSERT_FALSE(registry.valid(entity));
}
TYPED_TEST(SighMixin, Registry) {
using value_type = typename TestFixture::type;
entt::registry registry;
entt::sigh_mixin<entt::storage<value_type>> pool;
ASSERT_FALSE(pool);
pool.bind(registry);
ASSERT_TRUE(pool);
ASSERT_EQ(&pool.registry(), &registry);
ASSERT_EQ(&std::as_const(pool).registry(), &registry);
}
ENTT_DEBUG_TYPED_TEST(SighMixinDeathTest, Registry) {
using value_type = typename TestFixture::type;
entt::sigh_mixin<entt::storage<value_type>> pool;
ASSERT_DEATH([[maybe_unused]] auto &registry = pool.registry(), "");
ASSERT_DEATH([[maybe_unused]] const auto &registry = std::as_const(pool).registry(), "");
}
TYPED_TEST(SighMixin, CustomRegistry) {
using value_type = typename TestFixture::type;
using registry_type = test::custom_registry<test::entity>;
registry_type registry;
entt::basic_sigh_mixin<entt::basic_storage<value_type, test::entity>, registry_type> pool;
const std::array entity{registry.create(), registry.create()};
ASSERT_FALSE(pool);
pool.bind(registry);
ASSERT_TRUE(pool);
std::size_t on_construct{};
std::size_t on_destroy{};
pool.on_construct().template connect<&listener<registry_type>>(on_construct);
pool.on_destroy().template connect<&listener<registry_type>>(on_destroy);
pool.emplace(entity[0u]);
pool.emplace(entity[1u]);
ASSERT_EQ(on_construct, 2u);
ASSERT_EQ(on_destroy, 0u);
pool.clear();
ASSERT_EQ(on_construct, 2u);
ASSERT_EQ(on_destroy, 2u);
}
ENTT_DEBUG_TYPED_TEST(SighMixinDeathTest, CustomRegistry) {
using value_type = typename TestFixture::type;
using registry_type = test::custom_registry<test::entity>;
entt::basic_sigh_mixin<entt::basic_storage<value_type, test::entity>, registry_type> pool;
ASSERT_DEATH([[maybe_unused]] auto &registry = pool.registry(), "");
ASSERT_DEATH([[maybe_unused]] const auto &registry = std::as_const(pool).registry(), "");
}
TYPED_TEST(SighMixin, CustomAllocator) {
using value_type = typename TestFixture::type;
using storage_type = entt::sigh_mixin<entt::basic_storage<value_type, entt::entity, test::throwing_allocator<value_type>>>;
using registry_type = typename storage_type::registry_type;
const test::throwing_allocator<entt::entity> allocator{};
storage_type pool{allocator};
registry_type registry;
std::size_t on_construct{};
std::size_t on_destroy{};
pool.bind(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), allocator};
test::is_initialized(pool);
ASSERT_TRUE(pool.empty());
ASSERT_FALSE(other.empty());
ASSERT_NE(other.capacity(), 0u);
ASSERT_EQ(other.size(), 2u);
pool = std::move(other);
test::is_initialized(other);
ASSERT_FALSE(pool.empty());
ASSERT_TRUE(other.empty());
ASSERT_NE(pool.capacity(), 0u);
ASSERT_EQ(pool.size(), 2u);
other = {};
pool.swap(other);
pool = std::move(other);
test::is_initialized(other);
ASSERT_FALSE(pool.empty());
ASSERT_TRUE(other.empty());
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, 2u);
ASSERT_EQ(on_destroy, 2u);
}
TYPED_TEST(SighMixin, ThrowingAllocator) {
using value_type = typename TestFixture::type;
using storage_type = entt::sigh_mixin<entt::basic_storage<value_type, entt::entity, test::throwing_allocator<value_type>>>;
using registry_type = typename storage_type::registry_type;
storage_type pool{};
typename storage_type::base_type &base = pool;
registry_type registry;
constexpr auto packed_page_size = entt::component_traits<value_type>::page_size;
constexpr auto sparse_page_size = entt::entt_traits<entt::entity>::page_size;
std::size_t on_construct{};
std::size_t on_destroy{};
pool.bind(registry);
pool.on_construct().template connect<&listener<registry_type>>(on_construct);
pool.on_destroy().template connect<&listener<registry_type>>(on_destroy);
pool.get_allocator().template throw_counter<value_type>(0u);
ASSERT_THROW(pool.reserve(1u), test::throwing_allocator_exception);
ASSERT_EQ(pool.capacity(), 0u);
pool.get_allocator().template throw_counter<value_type>(1u);
ASSERT_THROW(pool.reserve(2 * packed_page_size), test::throwing_allocator_exception);
ASSERT_EQ(pool.capacity(), packed_page_size);
pool.shrink_to_fit();
ASSERT_EQ(pool.capacity(), 0u);
pool.get_allocator().template throw_counter<entt::entity>(0u);
ASSERT_THROW(pool.emplace(entt::entity{0}, 0), test::throwing_allocator_exception);
ASSERT_FALSE(pool.contains(entt::entity{0}));
ASSERT_TRUE(pool.empty());
pool.get_allocator().template throw_counter<entt::entity>(0u);
ASSERT_THROW(base.push(entt::entity{0}), test::throwing_allocator_exception);
ASSERT_FALSE(base.contains(entt::entity{0}));
ASSERT_TRUE(base.empty());
pool.get_allocator().template throw_counter<value_type>(0u);
ASSERT_THROW(pool.emplace(entt::entity{0}, 0), test::throwing_allocator_exception);
ASSERT_FALSE(pool.contains(entt::entity{0}));
ASSERT_NO_THROW(pool.compact());
ASSERT_TRUE(pool.empty());
pool.emplace(entt::entity{0}, 0);
const std::array entity{entt::entity{1}, entt::entity{sparse_page_size}};
pool.get_allocator().template throw_counter<entt::entity>(1u);
ASSERT_THROW(pool.insert(entity.begin(), entity.end(), value_type{0}), test::throwing_allocator_exception);
ASSERT_TRUE(pool.contains(entt::entity{1}));
ASSERT_FALSE(pool.contains(entt::entity{sparse_page_size}));
pool.erase(entt::entity{1});
const std::array component{value_type{1}, value_type{sparse_page_size}};
pool.get_allocator().template throw_counter<entt::entity>(0u);
pool.compact();
ASSERT_THROW(pool.insert(entity.begin(), entity.end(), component.begin()), test::throwing_allocator_exception);
ASSERT_TRUE(pool.contains(entt::entity{1}));
ASSERT_FALSE(pool.contains(entt::entity{sparse_page_size}));
ASSERT_EQ(on_construct, 1u);
ASSERT_EQ(on_destroy, 1u);
}
TEST(SighMixin, ThrowingComponent) {
using storage_type = entt::sigh_mixin<entt::storage<test::throwing_type>>;
using registry_type = typename storage_type::registry_type;
storage_type pool;
registry_type registry;
std::size_t on_construct{};
std::size_t on_destroy{};
pool.bind(registry);
pool.on_construct().connect<&listener<registry_type>>(on_construct);
pool.on_destroy().connect<&listener<registry_type>>(on_destroy);
const std::array entity{entt::entity{3}, entt::entity{1}};
const std::array<test::throwing_type, 2u> value{true, false};
// strong exception safety
ASSERT_THROW(pool.emplace(entity[0u], value[0u]), test::throwing_type_exception);
ASSERT_TRUE(pool.empty());
// basic exception safety
ASSERT_THROW(pool.insert(entity.begin(), entity.end(), value[0u]), test::throwing_type_exception);
ASSERT_EQ(pool.size(), 0u);
ASSERT_FALSE(pool.contains(entity[1u]));
// basic exception safety
ASSERT_THROW(pool.insert(entity.begin(), entity.end(), value.begin()), test::throwing_type_exception);
ASSERT_EQ(pool.size(), 0u);
ASSERT_FALSE(pool.contains(entity[1u]));
// basic exception safety
ASSERT_THROW(pool.insert(entity.rbegin(), entity.rend(), value.rbegin()), test::throwing_type_exception);
ASSERT_EQ(pool.size(), 1u);
ASSERT_TRUE(pool.contains(entity[1u]));
ASSERT_EQ(pool.get(entity[1u]), value[1u]);
pool.clear();
pool.emplace(entity[1u], value[0u].throw_on_copy());
pool.emplace(entity[0u], value[1u].throw_on_copy());
// basic exception safety
ASSERT_THROW(pool.erase(entity[1u]), test::throwing_type_exception);
ASSERT_EQ(pool.size(), 2u);
ASSERT_TRUE(pool.contains(entity[0u]));
ASSERT_TRUE(pool.contains(entity[1u]));
ASSERT_EQ(pool.index(entity[0u]), 1u);
ASSERT_EQ(pool.index(entity[1u]), 0u);
ASSERT_EQ(pool.get(entity[0u]), value[1u]);
// the element may have been moved but it's still there
ASSERT_EQ(pool.get(entity[1u]), value[0u]);
pool.get(entity[1u]).throw_on_copy(false);
pool.erase(entity[1u]);
ASSERT_EQ(pool.size(), 1u);
ASSERT_TRUE(pool.contains(entity[0u]));
ASSERT_FALSE(pool.contains(entity[1u]));
ASSERT_EQ(pool.index(entity[0u]), 0u);
ASSERT_EQ(pool.get(entity[0u]), value[1u]);
ASSERT_EQ(on_construct, 2u);
ASSERT_EQ(on_destroy, 3u);
}

View File

@@ -0,0 +1,992 @@
#include <array>
#include <iterator>
#include <type_traits>
#include <utility>
#include <vector>
#include <gtest/gtest.h>
#include <entt/core/any.hpp>
#include <entt/core/hashed_string.hpp>
#include <entt/entity/entity.hpp>
#include <entt/entity/mixin.hpp>
#include <entt/entity/registry.hpp>
#include <entt/entity/snapshot.hpp>
#include <entt/signal/sigh.hpp>
#include "../../common/config.h"
#include "../../common/empty.h"
#include "../../common/pointer_stable.h"
struct shadow {
entt::entity target{entt::null};
static void listener(entt::entity &elem, entt::registry &registry, const entt::entity entt) {
elem = registry.get<shadow>(entt).target;
}
};
TEST(BasicSnapshot, Constructors) {
static_assert(!std::is_default_constructible_v<entt::basic_snapshot<entt::registry>>, "Default constructible type not allowed");
static_assert(!std::is_copy_constructible_v<entt::basic_snapshot<entt::registry>>, "Copy constructible type not allowed");
static_assert(!std::is_copy_assignable_v<entt::basic_snapshot<entt::registry>>, "Copy assignable type not allowed");
static_assert(std::is_move_constructible_v<entt::basic_snapshot<entt::registry>>, "Move constructible type required");
static_assert(std::is_move_assignable_v<entt::basic_snapshot<entt::registry>>, "Move assignable type required");
const entt::registry registry;
entt::basic_snapshot snapshot{registry};
entt::basic_snapshot other{std::move(snapshot)};
ASSERT_NO_THROW(snapshot = std::move(other));
}
TEST(BasicSnapshot, GetEntityType) {
using namespace entt::literals;
using traits_type = entt::entt_traits<entt::entity>;
entt::registry registry;
const entt::basic_snapshot snapshot{registry};
const auto &storage = registry.storage<entt::entity>();
std::vector<entt::any> data{};
auto archive = [&data](auto &&elem) { data.emplace_back(std::forward<decltype(elem)>(elem)); };
snapshot.get<entt::entity>(archive);
ASSERT_EQ(data.size(), 2u);
ASSERT_NE(entt::any_cast<typename traits_type::entity_type>(data.data()), nullptr);
ASSERT_EQ(entt::any_cast<typename traits_type::entity_type>(data[0u]), storage.size());
ASSERT_NE(entt::any_cast<typename traits_type::entity_type>(&data[1u]), nullptr);
ASSERT_EQ(entt::any_cast<typename traits_type::entity_type>(data[1u]), storage.free_list());
constexpr auto number_of_entities = 3u;
std::array<entt::entity, number_of_entities> entity{};
registry.create(entity.begin(), entity.end());
registry.destroy(entity[1u]);
data.clear();
snapshot.get<entt::entity>(archive);
ASSERT_EQ(data.size(), 5u);
ASSERT_NE(entt::any_cast<typename traits_type::entity_type>(data.data()), nullptr);
ASSERT_EQ(entt::any_cast<typename traits_type::entity_type>(data[0u]), storage.size());
ASSERT_NE(entt::any_cast<typename traits_type::entity_type>(&data[1u]), nullptr);
ASSERT_EQ(entt::any_cast<typename traits_type::entity_type>(data[1u]), storage.free_list());
ASSERT_NE(entt::any_cast<entt::entity>(&data[2u]), nullptr);
ASSERT_EQ(entt::any_cast<entt::entity>(data[2u]), storage.data()[0u]);
ASSERT_NE(entt::any_cast<entt::entity>(&data[3u]), nullptr);
ASSERT_EQ(entt::any_cast<entt::entity>(data[3u]), storage.data()[1u]);
ASSERT_NE(entt::any_cast<entt::entity>(&data[4u]), nullptr);
ASSERT_EQ(entt::any_cast<entt::entity>(data[4u]), storage.data()[2u]);
}
TEST(BasicSnapshot, GetType) {
using namespace entt::literals;
using traits_type = entt::entt_traits<entt::entity>;
entt::registry registry;
const entt::basic_snapshot snapshot{registry};
const auto &storage = registry.storage<int>();
constexpr auto number_of_entities = 3u;
std::array<entt::entity, number_of_entities> entity{};
const std::array value{1, 2, 3};
registry.create(entity.begin(), entity.end());
registry.insert<int>(entity.begin(), entity.end(), value.begin());
registry.destroy(entity[1u]);
std::vector<entt::any> data{};
auto archive = [&data](auto &&elem) { data.emplace_back(std::forward<decltype(elem)>(elem)); };
snapshot.get<int>(archive, "other"_hs);
ASSERT_EQ(data.size(), 1u);
ASSERT_NE(entt::any_cast<typename traits_type::entity_type>(data.data()), nullptr);
ASSERT_EQ(entt::any_cast<typename traits_type::entity_type>(data[0u]), 0u);
data.clear();
snapshot.get<int>(archive);
ASSERT_EQ(data.size(), 5u);
ASSERT_NE(entt::any_cast<typename traits_type::entity_type>(data.data()), nullptr);
ASSERT_EQ(entt::any_cast<typename traits_type::entity_type>(data[0u]), storage.size());
ASSERT_NE(entt::any_cast<entt::entity>(&data[1u]), nullptr);
ASSERT_EQ(entt::any_cast<entt::entity>(data[1u]), entity[0u]);
ASSERT_NE(entt::any_cast<int>(&data[2u]), nullptr);
ASSERT_EQ(entt::any_cast<int>(data[2u]), value[0u]);
ASSERT_NE(entt::any_cast<entt::entity>(&data[3u]), nullptr);
ASSERT_EQ(entt::any_cast<entt::entity>(data[3u]), entity[2u]);
ASSERT_NE(entt::any_cast<int>(&data[4u]), nullptr);
ASSERT_EQ(entt::any_cast<int>(data[4u]), value[2u]);
}
TEST(BasicSnapshot, GetPointerStableType) {
using namespace entt::literals;
using traits_type = entt::entt_traits<entt::entity>;
entt::registry registry;
const entt::basic_snapshot snapshot{registry};
const auto &storage = registry.storage<test::pointer_stable>();
constexpr auto number_of_entities = 3u;
std::array<entt::entity, number_of_entities> entity{};
const std::array value{test::pointer_stable{1}, test::pointer_stable{2}, test::pointer_stable{3}};
registry.create(entity.begin(), entity.end());
registry.insert<test::pointer_stable>(entity.begin(), entity.end(), value.begin());
registry.destroy(entity[1u]);
std::vector<entt::any> data{};
auto archive = [&data](auto &&elem) { data.emplace_back(std::forward<decltype(elem)>(elem)); };
snapshot.get<test::pointer_stable>(archive, "other"_hs);
ASSERT_EQ(data.size(), 1u);
ASSERT_NE(entt::any_cast<typename traits_type::entity_type>(data.data()), nullptr);
ASSERT_EQ(entt::any_cast<typename traits_type::entity_type>(data[0u]), 0u);
data.clear();
snapshot.get<test::pointer_stable>(archive);
ASSERT_EQ(data.size(), 6u);
ASSERT_NE(entt::any_cast<typename traits_type::entity_type>(data.data()), nullptr);
ASSERT_EQ(entt::any_cast<typename traits_type::entity_type>(data[0u]), storage.size());
ASSERT_NE(entt::any_cast<entt::entity>(&data[1u]), nullptr);
ASSERT_EQ(entt::any_cast<entt::entity>(data[1u]), entity[0u]);
ASSERT_NE(entt::any_cast<test::pointer_stable>(&data[2u]), nullptr);
ASSERT_EQ(entt::any_cast<test::pointer_stable>(data[2u]), value[0u]);
ASSERT_NE(entt::any_cast<entt::entity>(&data[3u]), nullptr);
ASSERT_EQ(entt::any_cast<entt::entity>(data[3u]), static_cast<entt::entity>(entt::null));
ASSERT_NE(entt::any_cast<entt::entity>(&data[4u]), nullptr);
ASSERT_EQ(entt::any_cast<entt::entity>(data[4u]), entity[2u]);
ASSERT_NE(entt::any_cast<test::pointer_stable>(&data[5u]), nullptr);
ASSERT_EQ(entt::any_cast<test::pointer_stable>(data[5u]), value[2u]);
}
TEST(BasicSnapshot, GetEmptyType) {
using namespace entt::literals;
using traits_type = entt::entt_traits<entt::entity>;
entt::registry registry;
const entt::basic_snapshot snapshot{registry};
const auto &storage = registry.storage<test::empty>();
constexpr auto number_of_entities = 3u;
std::array<entt::entity, number_of_entities> entity{};
registry.create(entity.begin(), entity.end());
registry.insert<test::empty>(entity.begin(), entity.end());
registry.destroy(entity[1u]);
std::vector<entt::any> data{};
auto archive = [&data](auto &&elem) { data.emplace_back(std::forward<decltype(elem)>(elem)); };
snapshot.get<test::empty>(archive, "other"_hs);
ASSERT_EQ(data.size(), 1u);
ASSERT_NE(entt::any_cast<typename traits_type::entity_type>(data.data()), nullptr);
ASSERT_EQ(entt::any_cast<typename traits_type::entity_type>(data[0u]), 0u);
data.clear();
snapshot.get<test::empty>(archive);
ASSERT_EQ(data.size(), 3u);
ASSERT_NE(entt::any_cast<typename traits_type::entity_type>(data.data()), nullptr);
ASSERT_EQ(entt::any_cast<typename traits_type::entity_type>(data[0u]), storage.size());
ASSERT_NE(entt::any_cast<entt::entity>(&data[1u]), nullptr);
ASSERT_EQ(entt::any_cast<entt::entity>(data[1u]), entity[0u]);
ASSERT_NE(entt::any_cast<entt::entity>(&data[2u]), nullptr);
ASSERT_EQ(entt::any_cast<entt::entity>(data[2u]), entity[2u]);
}
TEST(BasicSnapshot, GetTypeSparse) {
using namespace entt::literals;
using traits_type = entt::entt_traits<entt::entity>;
entt::registry registry;
const entt::basic_snapshot snapshot{registry};
constexpr auto number_of_entities = 3u;
std::array<entt::entity, number_of_entities> entity{};
const std::array value{1, 2, 3};
registry.create(entity.begin(), entity.end());
registry.insert<int>(entity.begin(), entity.end(), value.begin());
registry.destroy(entity[1u]);
std::vector<entt::any> data{};
auto archive = [&data](auto &&elem) { data.emplace_back(std::forward<decltype(elem)>(elem)); };
snapshot.get<int>(archive, entity.begin(), entity.end(), "other"_hs);
ASSERT_EQ(data.size(), 1u);
ASSERT_NE(entt::any_cast<typename traits_type::entity_type>(data.data()), nullptr);
ASSERT_EQ(entt::any_cast<typename traits_type::entity_type>(data[0u]), 0u);
data.clear();
snapshot.get<int>(archive, entity.begin(), entity.end());
ASSERT_EQ(data.size(), 6u);
ASSERT_NE(entt::any_cast<typename traits_type::entity_type>(data.data()), nullptr);
ASSERT_EQ(entt::any_cast<typename traits_type::entity_type>(data[0u]), static_cast<typename traits_type::entity_type>(std::distance(entity.begin(), entity.end())));
ASSERT_NE(entt::any_cast<entt::entity>(&data[1u]), nullptr);
ASSERT_EQ(entt::any_cast<entt::entity>(data[1u]), entity[0u]);
ASSERT_NE(entt::any_cast<int>(&data[2u]), nullptr);
ASSERT_EQ(entt::any_cast<int>(data[2u]), value[0u]);
ASSERT_NE(entt::any_cast<entt::entity>(&data[3u]), nullptr);
ASSERT_EQ(entt::any_cast<entt::entity>(data[3u]), static_cast<entt::entity>(entt::null));
ASSERT_NE(entt::any_cast<entt::entity>(&data[4u]), nullptr);
ASSERT_EQ(entt::any_cast<entt::entity>(data[4u]), entity[2u]);
ASSERT_NE(entt::any_cast<int>(&data[5u]), nullptr);
ASSERT_EQ(entt::any_cast<int>(data[5u]), value[2u]);
}
TEST(BasicSnapshotLoader, Constructors) {
static_assert(!std::is_default_constructible_v<entt::basic_snapshot_loader<entt::registry>>, "Default constructible type not allowed");
static_assert(!std::is_copy_constructible_v<entt::basic_snapshot_loader<entt::registry>>, "Copy constructible type not allowed");
static_assert(!std::is_copy_assignable_v<entt::basic_snapshot_loader<entt::registry>>, "Copy assignable type not allowed");
static_assert(std::is_move_constructible_v<entt::basic_snapshot_loader<entt::registry>>, "Move constructible type required");
static_assert(std::is_move_assignable_v<entt::basic_snapshot_loader<entt::registry>>, "Move assignable type required");
entt::registry registry;
// helps stress the check in the constructor
registry.emplace<int>(registry.create(), 0);
registry.clear();
entt::basic_snapshot_loader loader{registry};
entt::basic_snapshot_loader other{std::move(loader)};
ASSERT_NO_THROW(loader = std::move(other));
}
ENTT_DEBUG_TEST(BasicSnapshotLoaderDeathTest, Constructors) {
entt::registry registry;
registry.emplace<int>(registry.create());
ASSERT_DEATH([[maybe_unused]] const entt::basic_snapshot_loader loader{registry}, "");
}
TEST(BasicSnapshotLoader, GetEntityType) {
using namespace entt::literals;
using traits_type = entt::entt_traits<entt::entity>;
entt::registry registry;
entt::basic_snapshot_loader loader{registry};
const auto &storage = registry.storage<entt::entity>();
std::vector<entt::any> data{};
auto archive = [&data, pos = 0u](auto &elem) mutable { elem = entt::any_cast<std::remove_reference_t<decltype(elem)>>(data[pos++]); };
const std::array entity{traits_type::construct(0u, 0u), traits_type::construct(2u, 0u), traits_type::construct(1u, 1u)};
ASSERT_FALSE(registry.valid(entity[0u]));
ASSERT_FALSE(registry.valid(entity[1u]));
ASSERT_FALSE(registry.valid(entity[2u]));
data.emplace_back(static_cast<typename traits_type::entity_type>(0u));
data.emplace_back(static_cast<typename traits_type::entity_type>(0u));
loader.get<entt::entity>(archive);
ASSERT_FALSE(registry.valid(entity[0u]));
ASSERT_FALSE(registry.valid(entity[1u]));
ASSERT_FALSE(registry.valid(entity[2u]));
ASSERT_EQ(storage.size(), 0u);
ASSERT_EQ(storage.free_list(), 0u);
data.emplace_back(static_cast<typename traits_type::entity_type>(3u));
data.emplace_back(static_cast<typename traits_type::entity_type>(2u));
data.emplace_back(entity[0u]);
data.emplace_back(entity[1u]);
data.emplace_back(entity[2u]);
loader.get<entt::entity>(archive);
ASSERT_TRUE(registry.valid(entity[0u]));
ASSERT_TRUE(registry.valid(entity[1u]));
ASSERT_FALSE(registry.valid(entity[2u]));
ASSERT_EQ(storage.size(), 3u);
ASSERT_EQ(storage.free_list(), 2u);
ASSERT_EQ(storage[0u], entity[0u]);
ASSERT_EQ(storage[1u], entity[1u]);
ASSERT_EQ(storage[2u], entity[2u]);
ASSERT_EQ(registry.create(), entity[2u]);
}
TEST(BasicSnapshotLoader, GetType) {
using namespace entt::literals;
using traits_type = entt::entt_traits<entt::entity>;
entt::registry registry;
entt::basic_snapshot_loader loader{registry};
const auto &storage = registry.storage<int>();
std::vector<entt::any> data{};
auto archive = [&data, pos = 0u](auto &elem) mutable { elem = entt::any_cast<std::remove_reference_t<decltype(elem)>>(data[pos++]); };
const std::array entity{traits_type::construct(0u, 0u), traits_type::construct(2u, 0u)};
const std::array value{1, 3};
ASSERT_FALSE(registry.valid(entity[0u]));
ASSERT_FALSE(registry.valid(entity[1u]));
data.emplace_back(static_cast<typename traits_type::entity_type>(1u));
data.emplace_back(entity[0u]);
data.emplace_back(value[0u]);
loader.get<int>(archive, "other"_hs);
ASSERT_TRUE(registry.valid(entity[0u]));
ASSERT_FALSE(registry.valid(entity[1u]));
ASSERT_EQ(storage.size(), 0u);
ASSERT_EQ(registry.storage<int>("other"_hs).size(), 1u);
data.emplace_back(static_cast<typename traits_type::entity_type>(2u));
data.emplace_back(entity[0u]);
data.emplace_back(value[0u]);
data.emplace_back(entity[1u]);
data.emplace_back(value[1u]);
loader.get<int>(archive);
ASSERT_TRUE(registry.valid(entity[0u]));
ASSERT_TRUE(registry.valid(entity[1u]));
ASSERT_EQ(storage.size(), 2u);
ASSERT_TRUE(storage.contains(entity[0u]));
ASSERT_TRUE(storage.contains(entity[1u]));
ASSERT_EQ(storage.get(entity[0u]), value[0u]);
ASSERT_EQ(storage.get(entity[1u]), value[1u]);
}
TEST(BasicSnapshotLoader, GetEmptyType) {
using namespace entt::literals;
using traits_type = entt::entt_traits<entt::entity>;
entt::registry registry;
entt::basic_snapshot_loader loader{registry};
const auto &storage = registry.storage<test::empty>();
std::vector<entt::any> data{};
auto archive = [&data, pos = 0u](auto &elem) mutable { elem = entt::any_cast<std::remove_reference_t<decltype(elem)>>(data[pos++]); };
const std::array entity{traits_type::construct(0u, 0u), traits_type::construct(2u, 0u)};
ASSERT_FALSE(registry.valid(entity[0u]));
ASSERT_FALSE(registry.valid(entity[1u]));
data.emplace_back(static_cast<typename traits_type::entity_type>(1u));
data.emplace_back(entity[0u]);
loader.get<test::empty>(archive, "other"_hs);
ASSERT_TRUE(registry.valid(entity[0u]));
ASSERT_FALSE(registry.valid(entity[1u]));
ASSERT_EQ(storage.size(), 0u);
ASSERT_EQ(registry.storage<test::empty>("other"_hs).size(), 1u);
data.emplace_back(static_cast<typename traits_type::entity_type>(2u));
data.emplace_back(entity[0u]);
data.emplace_back(entity[1u]);
loader.get<test::empty>(archive);
ASSERT_TRUE(registry.valid(entity[0u]));
ASSERT_TRUE(registry.valid(entity[1u]));
ASSERT_EQ(storage.size(), 2u);
ASSERT_TRUE(storage.contains(entity[0u]));
ASSERT_TRUE(storage.contains(entity[1u]));
}
TEST(BasicSnapshotLoader, GetTypeSparse) {
using namespace entt::literals;
using traits_type = entt::entt_traits<entt::entity>;
entt::registry registry;
entt::basic_snapshot_loader loader{registry};
const auto &storage = registry.storage<int>();
std::vector<entt::any> data{};
auto archive = [&data, pos = 0u](auto &elem) mutable { elem = entt::any_cast<std::remove_reference_t<decltype(elem)>>(data[pos++]); };
const std::array entity{traits_type::construct(0u, 0u), traits_type::construct(2u, 0u)};
const std::array value{1, 3};
ASSERT_FALSE(registry.valid(entity[0u]));
ASSERT_FALSE(registry.valid(entity[1u]));
data.emplace_back(static_cast<typename traits_type::entity_type>(2u));
data.emplace_back(static_cast<entt::entity>(entt::null));
data.emplace_back(entity[0u]);
data.emplace_back(value[0u]);
loader.get<int>(archive, "other"_hs);
ASSERT_TRUE(registry.valid(entity[0u]));
ASSERT_FALSE(registry.valid(entity[1u]));
ASSERT_EQ(storage.size(), 0u);
ASSERT_EQ(registry.storage<int>("other"_hs).size(), 1u);
data.emplace_back(static_cast<typename traits_type::entity_type>(3u));
data.emplace_back(entity[0u]);
data.emplace_back(value[0u]);
data.emplace_back(static_cast<entt::entity>(entt::null));
data.emplace_back(entity[1u]);
data.emplace_back(value[1u]);
loader.get<int>(archive);
ASSERT_TRUE(registry.valid(entity[0u]));
ASSERT_TRUE(registry.valid(entity[1u]));
ASSERT_EQ(storage.size(), 2u);
ASSERT_TRUE(storage.contains(entity[0u]));
ASSERT_TRUE(storage.contains(entity[1u]));
ASSERT_EQ(storage.get(entity[0u]), value[0u]);
ASSERT_EQ(storage.get(entity[1u]), value[1u]);
}
TEST(BasicSnapshotLoader, GetTypeWithListener) {
using traits_type = entt::entt_traits<entt::entity>;
entt::registry registry;
entt::basic_snapshot_loader loader{registry};
entt::entity check{entt::null};
std::vector<entt::any> data{};
auto archive = [&data, pos = 0u](auto &value) mutable { value = entt::any_cast<std::remove_reference_t<decltype(value)>>(data[pos++]); };
const auto entity{traits_type::construct(1u, 1u)};
const shadow value{entity};
ASSERT_FALSE(registry.valid(entity));
ASSERT_EQ(check, static_cast<entt::entity>(entt::null));
registry.on_construct<shadow>().connect<&shadow::listener>(check);
data.emplace_back(static_cast<typename traits_type::entity_type>(1u));
data.emplace_back(entity);
data.emplace_back(value);
loader.get<shadow>(archive);
ASSERT_TRUE(registry.valid(entity));
ASSERT_EQ(check, entity);
}
TEST(BasicSnapshotLoader, Orphans) {
using namespace entt::literals;
using traits_type = entt::entt_traits<entt::entity>;
entt::registry registry;
entt::basic_snapshot_loader loader{registry};
std::vector<entt::any> data{};
auto archive = [&data, pos = 0u](auto &elem) mutable { elem = entt::any_cast<std::remove_reference_t<decltype(elem)>>(data[pos++]); };
const std::array entity{traits_type::construct(0u, 0u), traits_type::construct(2u, 0u)};
const int value = 3;
ASSERT_FALSE(registry.valid(entity[0u]));
ASSERT_FALSE(registry.valid(entity[1u]));
data.emplace_back(static_cast<typename traits_type::entity_type>(2u));
data.emplace_back(static_cast<typename traits_type::entity_type>(2u));
data.emplace_back(entity[0u]);
data.emplace_back(entity[1u]);
data.emplace_back(static_cast<typename traits_type::entity_type>(1u));
data.emplace_back(entity[0u]);
data.emplace_back(value);
loader.get<entt::entity>(archive);
loader.get<int>(archive);
ASSERT_TRUE(registry.valid(entity[0u]));
ASSERT_TRUE(registry.valid(entity[1u]));
loader.orphans();
ASSERT_TRUE(registry.valid(entity[0u]));
ASSERT_FALSE(registry.valid(entity[1u]));
}
TEST(BasicContinuousLoader, Constructors) {
static_assert(!std::is_default_constructible_v<entt::basic_continuous_loader<entt::registry>>, "Default constructible type not allowed");
static_assert(!std::is_copy_constructible_v<entt::basic_continuous_loader<entt::registry>>, "Copy constructible type not allowed");
static_assert(!std::is_copy_assignable_v<entt::basic_continuous_loader<entt::registry>>, "Copy assignable type not allowed");
static_assert(std::is_move_constructible_v<entt::basic_continuous_loader<entt::registry>>, "Move constructible type required");
static_assert(std::is_move_assignable_v<entt::basic_continuous_loader<entt::registry>>, "Move assignable type required");
entt::registry registry;
entt::basic_continuous_loader loader{registry};
entt::basic_continuous_loader other{std::move(loader)};
ASSERT_NO_THROW(loader = std::move(other));
}
TEST(BasicContinuousLoader, GetEntityType) {
using namespace entt::literals;
using traits_type = entt::entt_traits<entt::entity>;
entt::registry registry;
entt::basic_continuous_loader loader{registry};
const auto &storage = registry.storage<entt::entity>();
std::vector<entt::any> data{};
auto archive = [&data, pos = 0u](auto &elem) mutable { elem = entt::any_cast<std::remove_reference_t<decltype(elem)>>(data[pos++]); };
const std::array entity{traits_type::construct(1u, 0u), traits_type::construct(0u, 0u), traits_type::construct(2u, 0u)};
ASSERT_FALSE(registry.valid(entity[0u]));
ASSERT_FALSE(registry.valid(entity[1u]));
ASSERT_FALSE(registry.valid(entity[2u]));
data.emplace_back(static_cast<typename traits_type::entity_type>(0u));
data.emplace_back(static_cast<typename traits_type::entity_type>(0u));
loader.get<entt::entity>(archive);
ASSERT_FALSE(registry.valid(entity[0u]));
ASSERT_FALSE(registry.valid(entity[1u]));
ASSERT_FALSE(registry.valid(entity[2u]));
ASSERT_FALSE(loader.contains(entity[0u]));
ASSERT_FALSE(loader.contains(entity[1u]));
ASSERT_FALSE(loader.contains(entity[2u]));
ASSERT_EQ(loader.map(entity[0u]), static_cast<entt::entity>(entt::null));
ASSERT_EQ(loader.map(entity[1u]), static_cast<entt::entity>(entt::null));
ASSERT_EQ(loader.map(entity[2u]), static_cast<entt::entity>(entt::null));
ASSERT_EQ(storage.size(), 0u);
ASSERT_EQ(storage.free_list(), 0u);
data.emplace_back(static_cast<typename traits_type::entity_type>(3u));
data.emplace_back(static_cast<typename traits_type::entity_type>(2u));
data.emplace_back(entity[0u]);
data.emplace_back(entity[1u]);
data.emplace_back(entity[2u]);
loader.get<entt::entity>(archive);
ASSERT_TRUE(loader.contains(entity[0u]));
ASSERT_TRUE(loader.contains(entity[1u]));
ASSERT_FALSE(loader.contains(entity[2u]));
ASSERT_NE(loader.map(entity[0u]), static_cast<entt::entity>(entt::null));
ASSERT_NE(loader.map(entity[1u]), static_cast<entt::entity>(entt::null));
ASSERT_EQ(loader.map(entity[2u]), static_cast<entt::entity>(entt::null));
ASSERT_TRUE(registry.valid(loader.map(entity[0u])));
ASSERT_TRUE(registry.valid(loader.map(entity[1u])));
ASSERT_EQ(storage.size(), 2u);
ASSERT_EQ(storage.free_list(), 2u);
ASSERT_EQ(storage[0u], loader.map(entity[0u]));
ASSERT_EQ(storage[1u], loader.map(entity[1u]));
ASSERT_EQ(registry.create(), entity[2u]);
data.emplace_back(static_cast<typename traits_type::entity_type>(3u));
data.emplace_back(static_cast<typename traits_type::entity_type>(3u));
data.emplace_back(entity[0u]);
data.emplace_back(entity[1u]);
data.emplace_back(entity[2u]);
loader.get<entt::entity>(archive);
ASSERT_TRUE(loader.contains(entity[0u]));
ASSERT_TRUE(loader.contains(entity[1u]));
ASSERT_TRUE(loader.contains(entity[2u]));
ASSERT_NE(loader.map(entity[0u]), static_cast<entt::entity>(entt::null));
ASSERT_NE(loader.map(entity[1u]), static_cast<entt::entity>(entt::null));
ASSERT_NE(loader.map(entity[2u]), static_cast<entt::entity>(entt::null));
ASSERT_TRUE(registry.valid(loader.map(entity[0u])));
ASSERT_TRUE(registry.valid(loader.map(entity[1u])));
ASSERT_TRUE(registry.valid(loader.map(entity[2u])));
ASSERT_EQ(storage.size(), 4u);
ASSERT_EQ(storage.free_list(), 4u);
ASSERT_EQ(storage[0u], loader.map(entity[0u]));
ASSERT_EQ(storage[1u], loader.map(entity[1u]));
ASSERT_EQ(storage[3u], loader.map(entity[2u]));
registry.destroy(loader.map(entity[1u]));
ASSERT_TRUE(loader.contains(entity[1u]));
ASSERT_NE(loader.map(entity[1u]), static_cast<entt::entity>(entt::null));
ASSERT_FALSE(registry.valid(loader.map(entity[1u])));
data.emplace_back(static_cast<typename traits_type::entity_type>(1u));
data.emplace_back(static_cast<typename traits_type::entity_type>(1u));
data.emplace_back(entity[1u]);
loader.get<entt::entity>(archive);
ASSERT_TRUE(loader.contains(entity[1u]));
ASSERT_NE(loader.map(entity[1u]), static_cast<entt::entity>(entt::null));
ASSERT_TRUE(registry.valid(loader.map(entity[1u])));
ASSERT_EQ(storage[3u], loader.map(entity[1u]));
data.emplace_back(static_cast<typename traits_type::entity_type>(3u));
data.emplace_back(static_cast<typename traits_type::entity_type>(1u));
data.emplace_back(entity[1u]);
data.emplace_back(entity[2u]);
data.emplace_back(entity[0u]);
loader.get<entt::entity>(archive);
ASSERT_FALSE(loader.contains(entity[0u]));
ASSERT_TRUE(loader.contains(entity[1u]));
ASSERT_FALSE(loader.contains(entity[2u]));
ASSERT_EQ(loader.map(entity[0u]), static_cast<entt::entity>(entt::null));
ASSERT_NE(loader.map(entity[1u]), static_cast<entt::entity>(entt::null));
ASSERT_EQ(loader.map(entity[2u]), static_cast<entt::entity>(entt::null));
ASSERT_TRUE(registry.valid(loader.map(entity[1u])));
ASSERT_EQ(storage.size(), 4u);
ASSERT_EQ(storage.free_list(), 2u);
ASSERT_EQ(storage[1u], loader.map(entity[1u]));
}
TEST(BasicContinuousLoader, GetType) {
using namespace entt::literals;
using traits_type = entt::entt_traits<entt::entity>;
entt::registry registry;
entt::basic_continuous_loader loader{registry};
const auto &storage = registry.storage<int>();
std::vector<entt::any> data{};
auto archive = [&data, pos = 0u](auto &elem) mutable { elem = entt::any_cast<std::remove_reference_t<decltype(elem)>>(data[pos++]); };
const std::array entity{traits_type::construct(0u, 0u), traits_type::construct(2u, 0u)};
const std::array value{1, 3};
ASSERT_FALSE(loader.contains(entity[0u]));
ASSERT_FALSE(loader.contains(entity[1u]));
ASSERT_FALSE(registry.valid(loader.map(entity[0u])));
ASSERT_FALSE(registry.valid(loader.map(entity[1u])));
data.emplace_back(static_cast<typename traits_type::entity_type>(1u));
data.emplace_back(entity[0u]);
data.emplace_back(value[0u]);
loader.get<int>(archive, "other"_hs);
ASSERT_TRUE(loader.contains(entity[0u]));
ASSERT_FALSE(loader.contains(entity[1u]));
ASSERT_TRUE(registry.valid(loader.map(entity[0u])));
ASSERT_FALSE(registry.valid(loader.map(entity[1u])));
ASSERT_EQ(storage.size(), 0u);
ASSERT_EQ(registry.storage<int>("other"_hs).size(), 1u);
data.emplace_back(static_cast<typename traits_type::entity_type>(2u));
data.emplace_back(entity[0u]);
data.emplace_back(value[0u]);
data.emplace_back(entity[1u]);
data.emplace_back(value[1u]);
loader.get<int>(archive);
ASSERT_TRUE(loader.contains(entity[0u]));
ASSERT_TRUE(loader.contains(entity[1u]));
ASSERT_TRUE(registry.valid(loader.map(entity[0u])));
ASSERT_TRUE(registry.valid(loader.map(entity[1u])));
ASSERT_EQ(storage.size(), 2u);
ASSERT_TRUE(storage.contains(loader.map(entity[0u])));
ASSERT_TRUE(storage.contains(loader.map(entity[1u])));
ASSERT_EQ(storage.get(loader.map(entity[0u])), value[0u]);
ASSERT_EQ(storage.get(loader.map(entity[1u])), value[1u]);
}
TEST(BasicContinuousLoader, GetTypeExtended) {
using namespace entt::literals;
using traits_type = entt::entt_traits<entt::entity>;
entt::registry registry;
entt::basic_continuous_loader loader{registry};
const auto &storage = registry.storage<shadow>();
std::vector<entt::any> data{};
const std::array entity{traits_type::construct(0u, 1u), traits_type::construct(1u, 1u)};
const shadow value{entity[0u]};
auto archive = [&loader, &data, pos = 0u](auto &elem) mutable {
elem = entt::any_cast<std::remove_reference_t<decltype(elem)>>(data[pos++]);
if constexpr(std::is_same_v<std::remove_reference_t<decltype(elem)>, shadow>) {
elem.target = loader.map(elem.target);
}
};
ASSERT_FALSE(loader.contains(entity[0u]));
ASSERT_FALSE(loader.contains(entity[1u]));
ASSERT_FALSE(registry.valid(loader.map(entity[0u])));
ASSERT_FALSE(registry.valid(loader.map(entity[1u])));
data.emplace_back(static_cast<typename traits_type::entity_type>(2u));
data.emplace_back(static_cast<typename traits_type::entity_type>(2u));
data.emplace_back(entity[0u]);
data.emplace_back(entity[1u]);
data.emplace_back(static_cast<typename traits_type::entity_type>(1u));
data.emplace_back(entity[1u]);
data.emplace_back(value);
loader.get<entt::entity>(archive);
loader.get<shadow>(archive);
ASSERT_TRUE(loader.contains(entity[0u]));
ASSERT_TRUE(loader.contains(entity[1u]));
ASSERT_TRUE(registry.valid(loader.map(entity[0u])));
ASSERT_TRUE(registry.valid(loader.map(entity[1u])));
ASSERT_FALSE(registry.valid(entity[0u]));
ASSERT_FALSE(registry.valid(entity[1u]));
ASSERT_EQ(storage.size(), 1u);
ASSERT_TRUE(storage.contains(loader.map(entity[1u])));
ASSERT_EQ(storage.get(loader.map(entity[1u])).target, loader.map(entity[0u]));
}
TEST(BasicContinuousLoader, GetEmptyType) {
using namespace entt::literals;
using traits_type = entt::entt_traits<entt::entity>;
entt::registry registry;
entt::basic_continuous_loader loader{registry};
const auto &storage = registry.storage<test::empty>();
std::vector<entt::any> data{};
auto archive = [&data, pos = 0u](auto &elem) mutable { elem = entt::any_cast<std::remove_reference_t<decltype(elem)>>(data[pos++]); };
const std::array entity{traits_type::construct(0u, 0u), traits_type::construct(2u, 0u)};
ASSERT_FALSE(loader.contains(entity[0u]));
ASSERT_FALSE(loader.contains(entity[1u]));
ASSERT_FALSE(registry.valid(loader.map(entity[0u])));
ASSERT_FALSE(registry.valid(loader.map(entity[1u])));
data.emplace_back(static_cast<typename traits_type::entity_type>(1u));
data.emplace_back(entity[0u]);
loader.get<test::empty>(archive, "other"_hs);
ASSERT_TRUE(loader.contains(entity[0u]));
ASSERT_FALSE(loader.contains(entity[1u]));
ASSERT_TRUE(registry.valid(loader.map(entity[0u])));
ASSERT_FALSE(registry.valid(loader.map(entity[1u])));
ASSERT_EQ(storage.size(), 0u);
ASSERT_EQ(registry.storage<test::empty>("other"_hs).size(), 1u);
data.emplace_back(static_cast<typename traits_type::entity_type>(2u));
data.emplace_back(entity[0u]);
data.emplace_back(entity[1u]);
loader.get<test::empty>(archive);
ASSERT_TRUE(loader.contains(entity[0u]));
ASSERT_TRUE(loader.contains(entity[1u]));
ASSERT_TRUE(registry.valid(loader.map(entity[0u])));
ASSERT_TRUE(registry.valid(loader.map(entity[1u])));
ASSERT_EQ(storage.size(), 2u);
ASSERT_TRUE(storage.contains(loader.map(entity[0u])));
ASSERT_TRUE(storage.contains(loader.map(entity[1u])));
}
TEST(BasicContinuousLoader, GetTypeSparse) {
using namespace entt::literals;
using traits_type = entt::entt_traits<entt::entity>;
entt::registry registry;
entt::basic_continuous_loader loader{registry};
const auto &storage = registry.storage<int>();
std::vector<entt::any> data{};
auto archive = [&data, pos = 0u](auto &elem) mutable { elem = entt::any_cast<std::remove_reference_t<decltype(elem)>>(data[pos++]); };
const std::array entity{traits_type::construct(0u, 0u), traits_type::construct(2u, 0u)};
const std::array value{1, 3};
ASSERT_FALSE(loader.contains(entity[0u]));
ASSERT_FALSE(loader.contains(entity[1u]));
ASSERT_FALSE(registry.valid(loader.map(entity[0u])));
ASSERT_FALSE(registry.valid(loader.map(entity[1u])));
data.emplace_back(static_cast<typename traits_type::entity_type>(2u));
data.emplace_back(static_cast<entt::entity>(entt::null));
data.emplace_back(entity[0u]);
data.emplace_back(value[0u]);
loader.get<int>(archive, "other"_hs);
ASSERT_TRUE(loader.contains(entity[0u]));
ASSERT_FALSE(loader.contains(entity[1u]));
ASSERT_TRUE(registry.valid(loader.map(entity[0u])));
ASSERT_FALSE(registry.valid(loader.map(entity[1u])));
ASSERT_EQ(storage.size(), 0u);
ASSERT_EQ(registry.storage<int>("other"_hs).size(), 1u);
data.emplace_back(static_cast<typename traits_type::entity_type>(3u));
data.emplace_back(entity[0u]);
data.emplace_back(value[0u]);
data.emplace_back(static_cast<entt::entity>(entt::null));
data.emplace_back(entity[1u]);
data.emplace_back(value[1u]);
loader.get<int>(archive);
ASSERT_TRUE(loader.contains(entity[0u]));
ASSERT_TRUE(loader.contains(entity[1u]));
ASSERT_TRUE(registry.valid(loader.map(entity[0u])));
ASSERT_TRUE(registry.valid(loader.map(entity[1u])));
ASSERT_EQ(storage.size(), 2u);
ASSERT_TRUE(storage.contains(loader.map(entity[0u])));
ASSERT_TRUE(storage.contains(loader.map(entity[1u])));
ASSERT_EQ(storage.get(loader.map(entity[0u])), value[0u]);
ASSERT_EQ(storage.get(loader.map(entity[1u])), value[1u]);
}
TEST(BasicContinuousLoader, GetTypeWithListener) {
using traits_type = entt::entt_traits<entt::entity>;
entt::registry registry;
entt::basic_continuous_loader loader{registry};
entt::entity check{entt::null};
std::vector<entt::any> data{};
auto archive = [&data, pos = 0u](auto &value) mutable { value = entt::any_cast<std::remove_reference_t<decltype(value)>>(data[pos++]); };
const auto entity{traits_type::construct(1u, 1u)};
const shadow value{entity};
ASSERT_FALSE(registry.valid(loader.map(entity)));
ASSERT_EQ(check, static_cast<entt::entity>(entt::null));
registry.on_construct<shadow>().connect<&shadow::listener>(check);
data.emplace_back(static_cast<typename traits_type::entity_type>(1u));
data.emplace_back(entity);
data.emplace_back(value);
loader.get<shadow>(archive);
ASSERT_TRUE(registry.valid(loader.map(entity)));
ASSERT_EQ(check, entity);
}
TEST(BasicContinuousLoader, Orphans) {
using namespace entt::literals;
using traits_type = entt::entt_traits<entt::entity>;
entt::registry registry;
entt::basic_continuous_loader loader{registry};
std::vector<entt::any> data{};
auto archive = [&data, pos = 0u](auto &elem) mutable { elem = entt::any_cast<std::remove_reference_t<decltype(elem)>>(data[pos++]); };
const std::array entity{traits_type::construct(0u, 0u), traits_type::construct(2u, 0u)};
const int value = 3;
ASSERT_FALSE(registry.valid(entity[0u]));
ASSERT_FALSE(registry.valid(entity[1u]));
data.emplace_back(static_cast<typename traits_type::entity_type>(2u));
data.emplace_back(static_cast<typename traits_type::entity_type>(2u));
data.emplace_back(entity[0u]);
data.emplace_back(entity[1u]);
data.emplace_back(static_cast<typename traits_type::entity_type>(1u));
data.emplace_back(entity[0u]);
data.emplace_back(value);
loader.get<entt::entity>(archive);
loader.get<int>(archive);
ASSERT_TRUE(loader.contains(entity[0u]));
ASSERT_TRUE(loader.contains(entity[1u]));
ASSERT_TRUE(registry.valid(loader.map(entity[0u])));
ASSERT_TRUE(registry.valid(loader.map(entity[1u])));
loader.orphans();
ASSERT_TRUE(loader.contains(entity[0u]));
ASSERT_TRUE(loader.contains(entity[1u]));
ASSERT_TRUE(registry.valid(loader.map(entity[0u])));
ASSERT_FALSE(registry.valid(loader.map(entity[1u])));
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,747 @@
#include <algorithm>
#include <array>
#include <functional>
#include <iterator>
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/iterator.hpp>
#include <entt/core/type_info.hpp>
#include <entt/entity/entity.hpp>
#include <entt/entity/storage.hpp>
#include "../../common/config.h"
#include "../../common/linter.hpp"
TEST(StorageEntity, Constructors) {
entt::storage<entt::entity> pool;
ASSERT_EQ(pool.policy(), entt::deletion_policy::swap_only);
ASSERT_NO_THROW([[maybe_unused]] auto alloc = pool.get_allocator());
ASSERT_EQ(pool.type(), entt::type_id<void>());
pool = entt::storage<entt::entity>{std::allocator<entt::entity>{}};
ASSERT_EQ(pool.policy(), entt::deletion_policy::swap_only);
ASSERT_NO_THROW([[maybe_unused]] auto alloc = pool.get_allocator());
ASSERT_EQ(pool.type(), entt::type_id<void>());
}
TEST(StorageEntity, Move) {
entt::storage<entt::entity> pool;
const std::array entity{entt::entity{3}, entt::entity{2}};
pool.generate(entity[0u]);
static_assert(std::is_move_constructible_v<decltype(pool)>, "Move constructible type required");
static_assert(std::is_move_assignable_v<decltype(pool)>, "Move assignable type required");
entt::storage<entt::entity> other{std::move(pool)};
test::is_initialized(pool);
ASSERT_TRUE(pool.empty());
ASSERT_FALSE(other.empty());
ASSERT_EQ(other.type(), entt::type_id<void>());
ASSERT_EQ(other.index(entity[0u]), 0u);
entt::storage<entt::entity> extended{std::move(other), std::allocator<entt::entity>{}};
test::is_initialized(other);
ASSERT_TRUE(other.empty());
ASSERT_FALSE(extended.empty());
ASSERT_EQ(extended.type(), entt::type_id<void>());
ASSERT_EQ(extended.index(entity[0u]), 0u);
pool = std::move(extended);
test::is_initialized(extended);
ASSERT_FALSE(pool.empty());
ASSERT_TRUE(other.empty());
ASSERT_TRUE(extended.empty());
ASSERT_EQ(pool.type(), entt::type_id<void>());
ASSERT_EQ(pool.index(entity[0u]), 0u);
other = entt::storage<entt::entity>{};
other.generate(entity[1u]);
other = std::move(pool);
test::is_initialized(pool);
ASSERT_FALSE(pool.empty());
ASSERT_FALSE(other.empty());
ASSERT_EQ(other.type(), entt::type_id<void>());
ASSERT_EQ(other.index(entity[0u]), 0u);
}
TEST(StorageEntity, Swap) {
entt::storage<entt::entity> pool;
entt::storage<entt::entity> other;
ASSERT_EQ(pool.type(), entt::type_id<void>());
ASSERT_EQ(other.type(), entt::type_id<void>());
pool.generate(entt::entity{4});
other.generate(entt::entity{2});
other.generate(entt::entity{1});
other.erase(entt::entity{2});
ASSERT_EQ(pool.size(), 1u);
ASSERT_EQ(other.size(), 2u);
pool.swap(other);
ASSERT_EQ(pool.type(), entt::type_id<void>());
ASSERT_EQ(other.type(), entt::type_id<void>());
ASSERT_EQ(pool.size(), 2u);
ASSERT_EQ(other.size(), 1u);
ASSERT_EQ(pool.index(entt::entity{1}), 0u);
ASSERT_EQ(other.index(entt::entity{4}), 0u);
}
TEST(StorageEntity, Getters) {
entt::storage<entt::entity> pool;
const entt::entity entity{4};
pool.generate(entity);
testing::StaticAssertTypeEq<decltype(pool.get({})), void>();
testing::StaticAssertTypeEq<decltype(std::as_const(pool).get({})), void>();
testing::StaticAssertTypeEq<decltype(pool.get_as_tuple({})), std::tuple<>>();
testing::StaticAssertTypeEq<decltype(std::as_const(pool).get_as_tuple({})), std::tuple<>>();
ASSERT_NO_THROW(pool.get(entity));
ASSERT_NO_THROW(std::as_const(pool).get(entity));
ASSERT_EQ(pool.get_as_tuple(entity), std::make_tuple());
ASSERT_EQ(std::as_const(pool).get_as_tuple(entity), std::make_tuple());
}
ENTT_DEBUG_TEST(StorageEntityDeathTest, Getters) {
entt::storage<entt::entity> pool;
const entt::entity entity{4};
ASSERT_DEATH(pool.get(entity), "");
ASSERT_DEATH(std::as_const(pool).get(entity), "");
ASSERT_DEATH([[maybe_unused]] const auto value = pool.get_as_tuple(entity), "");
ASSERT_DEATH([[maybe_unused]] const auto value = std::as_const(pool).get_as_tuple(entity), "");
}
TEST(StorageEntity, Generate) {
using traits_type = entt::entt_traits<entt::entity>;
entt::storage<entt::entity> pool;
std::array<entt::entity, 2u> entity{};
ASSERT_EQ(pool.emplace(), entt::entity{0});
ASSERT_EQ(pool.generate(entt::null), entt::entity{1});
ASSERT_EQ(pool.generate(entt::tombstone), entt::entity{2});
ASSERT_EQ(pool.emplace(entt::entity{0}), entt::entity{3});
ASSERT_EQ(pool.generate(traits_type::construct(1, 1)), entt::entity{4});
ASSERT_EQ(pool.generate(traits_type::construct(6, 3)), traits_type::construct(6, 3));
ASSERT_LT(pool.index(entt::entity{0}), pool.free_list());
ASSERT_LT(pool.index(entt::entity{1}), pool.free_list());
ASSERT_LT(pool.index(entt::entity{2}), pool.free_list());
ASSERT_LT(pool.index(entt::entity{3}), pool.free_list());
ASSERT_LT(pool.index(entt::entity{4}), pool.free_list());
ASSERT_EQ(pool.current(entt::entity{5}), traits_type::to_version(entt::tombstone));
ASSERT_LT(pool.index(traits_type::construct(6, 3)), pool.free_list());
ASSERT_EQ(pool.generate(traits_type::construct(5, 2)), traits_type::construct(5, 2));
ASSERT_EQ(pool.generate(traits_type::construct(5, 3)), entt::entity{7});
pool.erase(entt::entity{2});
ASSERT_EQ(pool.generate(), traits_type::construct(2, 1));
pool.erase(traits_type::construct(2, 1));
pool.generate(entity.begin(), entity.end());
ASSERT_EQ(entity[0u], traits_type::construct(2, 2));
ASSERT_EQ(entity[1u], entt::entity{8});
}
TEST(StorageEntity, GenerateFrom) {
entt::storage<entt::entity> pool;
std::array entity{entt::entity{0}, entt::entity{1}, entt::entity{2}};
ASSERT_EQ(pool.generate(), entity[0u]);
pool.start_from(entity[2u]);
ASSERT_EQ(pool.generate(), entity[2u]);
ASSERT_FALSE(pool.contains(entity[1u]));
}
TEST(StorageEntity, GenerateInUse) {
entt::storage<entt::entity> pool;
std::array<entt::entity, 2u> entity{};
const entt::entity other{1};
ASSERT_EQ(pool.generate(other), other);
ASSERT_EQ(pool.generate(), entt::entity{0});
ASSERT_EQ(pool.generate(), entt::entity{2});
pool.clear();
ASSERT_EQ(pool.generate(other), other);
pool.generate(entity.begin(), entity.end());
ASSERT_EQ(entity[0u], entt::entity{0});
ASSERT_EQ(entity[1u], entt::entity{2});
}
TEST(StorageEntity, TryGenerate) {
using traits_type = entt::entt_traits<entt::entity>;
entt::storage<entt::entity> pool;
ASSERT_EQ(*pool.push(entt::null), entt::entity{0});
ASSERT_EQ(*pool.push(entt::tombstone), entt::entity{1});
ASSERT_EQ(*pool.push(entt::entity{0}), entt::entity{2});
ASSERT_EQ(*pool.push(traits_type::construct(1, 1)), entt::entity{3});
ASSERT_EQ(*pool.push(traits_type::construct(5, 3)), traits_type::construct(5, 3));
ASSERT_LT(pool.index(entt::entity{0}), pool.free_list());
ASSERT_LT(pool.index(entt::entity{1}), pool.free_list());
ASSERT_LT(pool.index(entt::entity{2}), pool.free_list());
ASSERT_LT(pool.index(entt::entity{3}), pool.free_list());
ASSERT_EQ(pool.current(entt::entity{4}), traits_type::to_version(entt::tombstone));
ASSERT_LT(pool.index(traits_type::construct(5, 3)), pool.free_list());
ASSERT_EQ(*pool.push(traits_type::construct(4, 2)), traits_type::construct(4, 2));
ASSERT_EQ(*pool.push(traits_type::construct(4, 3)), entt::entity{6});
const std::array entity{entt::entity{1}, traits_type::construct(5, 3)};
pool.erase(entity.begin(), entity.end());
pool.erase(entt::entity{2});
ASSERT_EQ(pool.current(entity[0u]), 1);
ASSERT_EQ(pool.current(entity[1u]), 4);
ASSERT_EQ(pool.current(entt::entity{2}), 1);
ASSERT_LT(pool.index(entt::entity{0}), pool.free_list());
ASSERT_GE(pool.index(traits_type::construct(1, 1)), pool.free_list());
ASSERT_GE(pool.index(traits_type::construct(2, 1)), pool.free_list());
ASSERT_LT(pool.index(entt::entity{3}), pool.free_list());
ASSERT_LT(pool.index(traits_type::construct(4, 2)), pool.free_list());
ASSERT_GE(pool.index(traits_type::construct(5, 4)), pool.free_list());
ASSERT_EQ(*pool.push(entt::null), traits_type::construct(2, 1));
ASSERT_EQ(*pool.push(traits_type::construct(1, 3)), traits_type::construct(1, 3));
ASSERT_EQ(*pool.push(entt::null), traits_type::construct(5, 4));
ASSERT_EQ(*pool.push(entt::null), entt::entity{7});
}
TEST(StorageEntity, TryGenerateInUse) {
entt::storage<entt::entity> pool;
std::array<entt::entity, 2u> entity{entt::entity{0}, entt::entity{0}};
const entt::entity other{1};
ASSERT_EQ(*pool.push(other), other);
ASSERT_EQ(*pool.push(other), entt::entity{0});
ASSERT_EQ(*pool.push(other), entt::entity{2});
pool.clear();
ASSERT_EQ(*pool.push(other), other);
auto it = pool.push(entity.begin(), entity.end());
ASSERT_EQ(*it, entt::entity{2});
ASSERT_EQ(*(++it), entt::entity{0});
}
TEST(StorageEntity, Patch) {
entt::storage<entt::entity> pool;
const auto entity = pool.generate();
int counter = 0;
auto callback = [&counter]() { ++counter; };
ASSERT_EQ(counter, 0);
pool.patch(entity);
pool.patch(entity, callback);
pool.patch(entity, callback, callback);
ASSERT_EQ(counter, 3);
}
ENTT_DEBUG_TEST(StorageEntityDeathTest, Patch) {
entt::storage<entt::entity> pool;
ASSERT_DEATH(pool.patch(entt::null), "");
}
TEST(StorageEntity, Insert) {
entt::storage<entt::entity> pool;
std::array<entt::entity, 2u> entity{};
pool.insert(entity.begin(), entity.end());
ASSERT_TRUE(pool.contains(entity[0u]));
ASSERT_TRUE(pool.contains(entity[1u]));
ASSERT_FALSE(pool.empty());
ASSERT_EQ(pool.size(), 2u);
ASSERT_EQ(pool.free_list(), 2u);
pool.erase(entity.begin(), entity.end());
ASSERT_FALSE(pool.empty());
ASSERT_EQ(pool.size(), 2u);
ASSERT_EQ(pool.free_list(), 0u);
pool.generate(entity.begin(), entity.begin() + 1u);
ASSERT_TRUE(pool.contains(entity[0u]));
ASSERT_FALSE(pool.contains(entity[1u]));
ASSERT_FALSE(pool.empty());
ASSERT_EQ(pool.size(), 2u);
ASSERT_EQ(pool.free_list(), 1u);
}
TEST(StorageEntity, Pack) {
entt::storage<entt::entity> pool;
std::array entity{entt::entity{1}, entt::entity{3}, entt::entity{4}, entt::entity{2}};
pool.push(entity.begin(), entity.end());
pool.erase(entity[3u]);
std::swap(entity[0u], entity[1u]);
const auto to = pool.sort_as(entity.begin() + 1u, entity.end());
auto from = pool.each().cbegin().base();
ASSERT_NE(from, pool.cbegin());
ASSERT_NE(from, pool.cend());
ASSERT_NE(to, pool.cend());
ASSERT_EQ(to + 1u, pool.cend());
ASSERT_EQ(*from++, entity[1u]);
ASSERT_EQ(*from++, entity[2u]);
ASSERT_NE(from, pool.cend());
ASSERT_EQ(*from++, entity[0u]);
ASSERT_EQ(from, pool.cend());
}
TEST(StorageEntity, FreeList) {
entt::storage<entt::entity> pool;
pool.generate(entt::entity{0});
ASSERT_EQ(pool.size(), 1u);
ASSERT_EQ(pool.free_list(), 1u);
pool.free_list(0u);
ASSERT_EQ(pool.size(), 1u);
ASSERT_EQ(pool.free_list(), 0u);
pool.free_list(1u);
ASSERT_EQ(pool.size(), 1u);
ASSERT_EQ(pool.free_list(), 1u);
}
ENTT_DEBUG_TEST(StorageEntityDeathTest, FreeList) {
entt::storage<entt::entity> pool;
pool.generate(entt::entity{0});
ASSERT_DEATH(pool.free_list(2u), "");
}
TEST(StorageEntity, Iterable) {
using iterator = typename entt::storage<entt::entity>::iterable::iterator;
testing::StaticAssertTypeEq<typename iterator::value_type, std::tuple<entt::entity>>();
testing::StaticAssertTypeEq<typename iterator::pointer, entt::input_iterator_pointer<std::tuple<entt::entity>>>();
testing::StaticAssertTypeEq<typename iterator::reference, typename iterator::value_type>();
entt::storage<entt::entity> pool;
pool.generate(entt::entity{1});
pool.generate(entt::entity{3});
pool.generate(entt::entity{4});
pool.erase(entt::entity{3});
auto iterable = pool.each();
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_NE(begin.base(), pool.begin());
ASSERT_EQ(begin.base(), pool.end() - static_cast<typename iterator::difference_type>(pool.free_list()));
ASSERT_EQ(end.base(), pool.end());
ASSERT_EQ(std::get<0>(*begin.operator->().operator->()), entt::entity{4});
ASSERT_EQ(std::get<0>(*begin), entt::entity{4});
ASSERT_EQ(begin++, iterable.begin());
ASSERT_EQ(begin.base(), pool.end() - 1);
ASSERT_EQ(++begin, iterable.end());
ASSERT_EQ(begin.base(), pool.end());
for(auto [entity]: iterable) {
testing::StaticAssertTypeEq<decltype(entity), entt::entity>();
ASSERT_TRUE(entity != entt::entity{3});
}
}
TEST(StorageEntity, ConstIterable) {
using iterator = typename entt::storage<entt::entity>::const_iterable::iterator;
testing::StaticAssertTypeEq<typename iterator::value_type, std::tuple<entt::entity>>();
testing::StaticAssertTypeEq<typename iterator::pointer, entt::input_iterator_pointer<std::tuple<entt::entity>>>();
testing::StaticAssertTypeEq<typename iterator::reference, typename iterator::value_type>();
entt::storage<entt::entity> pool;
pool.generate(entt::entity{1});
pool.generate(entt::entity{3});
pool.generate(entt::entity{4});
pool.erase(entt::entity{3});
auto iterable = std::as_const(pool).each();
iterator end{iterable.cbegin()};
iterator begin{};
begin = iterable.cend();
std::swap(begin, end);
ASSERT_EQ(begin, iterable.cbegin());
ASSERT_EQ(end, iterable.cend());
ASSERT_NE(begin, end);
ASSERT_NE(begin.base(), pool.begin());
ASSERT_EQ(begin.base(), pool.end() - static_cast<typename iterator::difference_type>(pool.free_list()));
ASSERT_EQ(end.base(), pool.end());
ASSERT_EQ(std::get<0>(*begin.operator->().operator->()), entt::entity{4});
ASSERT_EQ(std::get<0>(*begin), entt::entity{4});
ASSERT_EQ(begin++, iterable.begin());
ASSERT_EQ(begin.base(), pool.end() - 1);
ASSERT_EQ(++begin, iterable.end());
ASSERT_EQ(begin.base(), pool.end());
for(auto [entity]: iterable) {
testing::StaticAssertTypeEq<decltype(entity), entt::entity>();
ASSERT_TRUE(entity != entt::entity{3});
}
}
TEST(StorageEntity, IterableIteratorConversion) {
entt::storage<entt::entity> pool;
pool.generate(entt::entity{3});
const typename entt::storage<entt::entity>::iterable::iterator it = pool.each().begin();
typename entt::storage<entt::entity>::const_iterable::iterator cit = it;
testing::StaticAssertTypeEq<decltype(*it), std::tuple<entt::entity>>();
testing::StaticAssertTypeEq<decltype(*cit), std::tuple<entt::entity>>();
ASSERT_EQ(it, cit);
ASSERT_NE(++cit, it);
}
TEST(StorageEntity, IterableAlgorithmCompatibility) {
entt::storage<entt::entity> pool;
pool.generate(entt::entity{3});
const auto iterable = pool.each();
const auto it = std::find_if(iterable.begin(), iterable.end(), [](auto args) { return std::get<0>(args) == entt::entity{3}; });
ASSERT_EQ(std::get<0>(*it), entt::entity{3});
}
TEST(StorageEntity, ReverseIterable) {
using iterator = typename entt::storage<entt::entity>::reverse_iterable::iterator;
testing::StaticAssertTypeEq<typename iterator::value_type, std::tuple<entt::entity>>();
testing::StaticAssertTypeEq<typename iterator::pointer, entt::input_iterator_pointer<std::tuple<entt::entity>>>();
testing::StaticAssertTypeEq<typename iterator::reference, typename iterator::value_type>();
entt::storage<entt::entity> pool;
pool.generate(entt::entity{1});
pool.generate(entt::entity{3});
pool.generate(entt::entity{4});
pool.erase(entt::entity{3});
auto iterable = pool.reach();
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.base(), pool.rbegin());
ASSERT_EQ(end.base(), pool.rbegin() + static_cast<typename iterator::difference_type>(pool.free_list()));
ASSERT_NE(end.base(), pool.rend());
ASSERT_EQ(std::get<0>(*begin.operator->().operator->()), entt::entity{1});
ASSERT_EQ(std::get<0>(*begin), entt::entity{1});
ASSERT_EQ(begin++, iterable.begin());
ASSERT_EQ(begin.base(), pool.rbegin() + 1);
ASSERT_EQ(++begin, iterable.end());
ASSERT_EQ(begin.base(), pool.rbegin() + 2);
for(auto [entity]: iterable) {
testing::StaticAssertTypeEq<decltype(entity), entt::entity>();
ASSERT_TRUE(entity != entt::entity{3});
}
}
TEST(StorageEntity, ReverseConstIterable) {
using iterator = typename entt::storage<entt::entity>::const_reverse_iterable::iterator;
testing::StaticAssertTypeEq<typename iterator::value_type, std::tuple<entt::entity>>();
testing::StaticAssertTypeEq<typename iterator::pointer, entt::input_iterator_pointer<std::tuple<entt::entity>>>();
testing::StaticAssertTypeEq<typename iterator::reference, typename iterator::value_type>();
entt::storage<entt::entity> pool;
pool.generate(entt::entity{1});
pool.generate(entt::entity{3});
pool.generate(entt::entity{4});
pool.erase(entt::entity{3});
auto iterable = std::as_const(pool).reach();
iterator end{iterable.cbegin()};
iterator begin{};
begin = iterable.cend();
std::swap(begin, end);
ASSERT_EQ(begin, iterable.cbegin());
ASSERT_EQ(end, iterable.cend());
ASSERT_NE(begin, end);
ASSERT_EQ(begin.base(), pool.rbegin());
ASSERT_EQ(end.base(), pool.rbegin() + static_cast<typename iterator::difference_type>(pool.free_list()));
ASSERT_NE(end.base(), pool.rend());
ASSERT_EQ(std::get<0>(*begin.operator->().operator->()), entt::entity{1});
ASSERT_EQ(std::get<0>(*begin), entt::entity{1});
ASSERT_EQ(begin++, iterable.begin());
ASSERT_EQ(begin.base(), pool.rbegin() + 1);
ASSERT_EQ(++begin, iterable.end());
ASSERT_EQ(begin.base(), pool.rbegin() + 2);
for(auto [entity]: iterable) {
testing::StaticAssertTypeEq<decltype(entity), entt::entity>();
ASSERT_TRUE(entity != entt::entity{3});
}
}
TEST(StorageEntity, ReverseIterableIteratorConversion) {
entt::storage<entt::entity> pool;
pool.generate(entt::entity{3});
const typename entt::storage<entt::entity>::reverse_iterable::iterator it = pool.reach().begin();
typename entt::storage<entt::entity>::const_reverse_iterable::iterator cit = it;
testing::StaticAssertTypeEq<decltype(*it), std::tuple<entt::entity>>();
testing::StaticAssertTypeEq<decltype(*cit), std::tuple<entt::entity>>();
ASSERT_EQ(it, cit);
ASSERT_NE(++cit, it);
}
TEST(StorageEntity, ReverseIterableAlgorithmCompatibility) {
entt::storage<entt::entity> pool;
pool.generate(entt::entity{3});
const auto iterable = pool.reach();
const auto it = std::find_if(iterable.begin(), iterable.end(), [](auto args) { return std::get<0>(args) == entt::entity{3}; });
ASSERT_EQ(std::get<0>(*it), entt::entity{3});
}
TEST(StorageEntity, SortOrdered) {
entt::storage<entt::entity> pool;
const std::array entity{entt::entity{16}, entt::entity{8}, entt::entity{4}, entt::entity{2}, entt::entity{1}};
pool.push(entity.begin(), entity.end());
pool.sort(std::less{});
ASSERT_TRUE(std::equal(entity.rbegin(), entity.rend(), pool.begin(), pool.end()));
}
TEST(StorageEntity, SortReverse) {
entt::storage<entt::entity> pool;
const std::array entity{entt::entity{1}, entt::entity{2}, entt::entity{4}, entt::entity{8}, entt::entity{16}};
pool.push(entity.begin(), entity.end());
pool.sort(std::less{});
ASSERT_TRUE(std::equal(entity.begin(), entity.end(), pool.begin(), pool.end()));
}
TEST(StorageEntity, SortUnordered) {
entt::storage<entt::entity> pool;
const std::array entity{entt::entity{4}, entt::entity{2}, entt::entity{1}, entt::entity{8}, entt::entity{16}};
pool.push(entity.begin(), entity.end());
pool.sort(std::less{});
ASSERT_EQ(pool.data()[0u], entity[4u]);
ASSERT_EQ(pool.data()[1u], entity[3u]);
ASSERT_EQ(pool.data()[2u], entity[0u]);
ASSERT_EQ(pool.data()[3u], entity[1u]);
ASSERT_EQ(pool.data()[4u], entity[2u]);
}
TEST(StorageEntity, SortN) {
entt::storage<entt::entity> pool;
const std::array entity{entt::entity{2}, entt::entity{4}, entt::entity{1}, entt::entity{8}, entt::entity{16}};
pool.push(entity.begin(), entity.end());
pool.sort_n(0u, std::less{});
ASSERT_TRUE(std::equal(entity.rbegin(), entity.rend(), pool.begin(), pool.end()));
pool.sort_n(2u, std::less{});
ASSERT_EQ(pool.data()[0u], entity[1u]);
ASSERT_EQ(pool.data()[1u], entity[0u]);
ASSERT_EQ(pool.data()[2u], entity[2u]);
const auto length = 5u;
pool.sort_n(length, std::less{});
ASSERT_EQ(pool.data()[0u], entity[4u]);
ASSERT_EQ(pool.data()[1u], entity[3u]);
ASSERT_EQ(pool.data()[2u], entity[1u]);
ASSERT_EQ(pool.data()[3u], entity[0u]);
ASSERT_EQ(pool.data()[4u], entity[2u]);
}
TEST(StorageEntity, SortAsDisjoint) {
entt::storage<entt::entity> lhs;
const entt::storage<entt::entity> rhs;
const std::array entity{entt::entity{1}, entt::entity{2}, entt::entity{4}};
lhs.push(entity.begin(), entity.end());
ASSERT_TRUE(std::equal(entity.rbegin(), entity.rend(), lhs.begin(), lhs.end()));
lhs.sort_as(rhs.begin(), rhs.end());
ASSERT_TRUE(std::equal(entity.rbegin(), entity.rend(), lhs.begin(), lhs.end()));
}
TEST(StorageEntity, SortAsOverlap) {
entt::storage<entt::entity> lhs;
entt::storage<entt::entity> rhs;
const std::array lhs_entity{entt::entity{1}, entt::entity{2}, entt::entity{4}};
const std::array rhs_entity{entt::entity{2}};
lhs.push(lhs_entity.begin(), lhs_entity.end());
rhs.push(rhs_entity.begin(), rhs_entity.end());
ASSERT_TRUE(std::equal(lhs_entity.rbegin(), lhs_entity.rend(), lhs.begin(), lhs.end()));
ASSERT_TRUE(std::equal(rhs_entity.rbegin(), rhs_entity.rend(), rhs.begin(), rhs.end()));
lhs.sort_as(rhs.begin(), rhs.end());
ASSERT_EQ(lhs.data()[0u], lhs_entity[0u]);
ASSERT_EQ(lhs.data()[1u], lhs_entity[2u]);
ASSERT_EQ(lhs.data()[2u], lhs_entity[1u]);
}
TEST(StorageEntity, SortAsOrdered) {
entt::storage<entt::entity> lhs;
entt::storage<entt::entity> rhs;
const std::array lhs_entity{entt::entity{1}, entt::entity{2}, entt::entity{4}, entt::entity{8}, entt::entity{16}};
const std::array rhs_entity{entt::entity{32}, entt::entity{1}, entt::entity{2}, entt::entity{4}, entt::entity{8}, entt::entity{16}};
lhs.push(lhs_entity.begin(), lhs_entity.end());
rhs.push(rhs_entity.begin(), rhs_entity.end());
ASSERT_TRUE(std::equal(lhs_entity.rbegin(), lhs_entity.rend(), lhs.begin(), lhs.end()));
ASSERT_TRUE(std::equal(rhs_entity.rbegin(), rhs_entity.rend(), rhs.begin(), rhs.end()));
rhs.sort_as(lhs.begin(), lhs.end());
ASSERT_TRUE(std::equal(rhs_entity.rbegin(), rhs_entity.rend(), rhs.begin(), rhs.end()));
}
TEST(StorageEntity, SortAsReverse) {
entt::storage<entt::entity> lhs;
entt::storage<entt::entity> rhs;
const std::array lhs_entity{entt::entity{1}, entt::entity{2}, entt::entity{4}, entt::entity{8}, entt::entity{16}};
const std::array rhs_entity{entt::entity{16}, entt::entity{8}, entt::entity{4}, entt::entity{2}, entt::entity{1}, entt::entity{32}};
lhs.push(lhs_entity.begin(), lhs_entity.end());
rhs.push(rhs_entity.begin(), rhs_entity.end());
ASSERT_TRUE(std::equal(lhs_entity.rbegin(), lhs_entity.rend(), lhs.begin(), lhs.end()));
ASSERT_TRUE(std::equal(rhs_entity.rbegin(), rhs_entity.rend(), rhs.begin(), rhs.end()));
rhs.sort_as(lhs.begin(), lhs.end());
ASSERT_EQ(rhs.data()[0u], rhs_entity[5u]);
ASSERT_EQ(rhs.data()[1u], rhs_entity[4u]);
ASSERT_EQ(rhs.data()[2u], rhs_entity[3u]);
ASSERT_EQ(rhs.data()[3u], rhs_entity[2u]);
ASSERT_EQ(rhs.data()[4u], rhs_entity[1u]);
ASSERT_EQ(rhs.data()[5u], rhs_entity[0u]);
}
TEST(StorageEntity, SortAsUnordered) {
entt::storage<entt::entity> lhs;
entt::storage<entt::entity> rhs;
const std::array lhs_entity{entt::entity{1}, entt::entity{2}, entt::entity{4}, entt::entity{8}, entt::entity{16}};
const std::array rhs_entity{entt::entity{4}, entt::entity{2}, entt::entity{32}, entt::entity{1}, entt::entity{8}, entt::entity{16}};
lhs.push(lhs_entity.begin(), lhs_entity.end());
rhs.push(rhs_entity.begin(), rhs_entity.end());
ASSERT_TRUE(std::equal(lhs_entity.rbegin(), lhs_entity.rend(), lhs.begin(), lhs.end()));
ASSERT_TRUE(std::equal(rhs_entity.rbegin(), rhs_entity.rend(), rhs.begin(), rhs.end()));
rhs.sort_as(lhs.begin(), lhs.end());
ASSERT_EQ(rhs.data()[0u], rhs_entity[2u]);
ASSERT_EQ(rhs.data()[1u], rhs_entity[3u]);
ASSERT_EQ(rhs.data()[2u], rhs_entity[1u]);
ASSERT_EQ(rhs.data()[3u], rhs_entity[0u]);
ASSERT_EQ(rhs.data()[4u], rhs_entity[4u]);
ASSERT_EQ(rhs.data()[5u], rhs_entity[5u]);
}

View File

@@ -0,0 +1,730 @@
#include <algorithm>
#include <array>
#include <functional>
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/iterator.hpp>
#include <entt/core/type_info.hpp>
#include <entt/entity/component.hpp>
#include <entt/entity/entity.hpp>
#include <entt/entity/storage.hpp>
#include "../../common/config.h"
#include "../../common/empty.h"
#include "../../common/linter.hpp"
template<typename Type>
struct StorageNoInstance: testing::Test {
static_assert(entt::component_traits<Type>::page_size == 0u, "Non-empty type not allowed");
using type = Type;
static auto emplace_instance(entt::storage<type> &pool, const entt::entity entt) {
if constexpr(std::is_void_v<type>) {
return pool.emplace(entt);
} else {
return pool.emplace(entt, type{});
}
}
template<typename It>
static auto insert_instance(entt::storage<type> &pool, const It from, const It to) {
if constexpr(std::is_void_v<type>) {
return pool.insert(from, to);
} else {
const std::array<type, 2u> value{};
return pool.insert(from, to, value.begin());
}
}
static auto push_instance(entt::storage<type> &pool, const entt::entity entt) {
if constexpr(std::is_void_v<type>) {
return pool.push(entt, nullptr);
} else {
type instance{};
return pool.push(entt, &instance);
}
}
};
template<typename Type>
using StorageNoInstanceDeathTest = StorageNoInstance<Type>;
using StorageNoInstanceTypes = ::testing::Types<test::empty, void>;
TYPED_TEST_SUITE(StorageNoInstance, StorageNoInstanceTypes, );
TYPED_TEST_SUITE(StorageNoInstanceDeathTest, StorageNoInstanceTypes, );
TYPED_TEST(StorageNoInstance, Constructors) {
using value_type = typename TestFixture::type;
entt::storage<value_type> pool;
ASSERT_EQ(pool.policy(), entt::deletion_policy::swap_and_pop);
ASSERT_NO_THROW([[maybe_unused]] auto alloc = pool.get_allocator());
ASSERT_EQ(pool.type(), entt::type_id<value_type>());
pool = entt::storage<value_type>{std::allocator<value_type>{}};
ASSERT_EQ(pool.policy(), entt::deletion_policy::swap_and_pop);
ASSERT_NO_THROW([[maybe_unused]] auto alloc = pool.get_allocator());
ASSERT_EQ(pool.type(), entt::type_id<value_type>());
}
TYPED_TEST(StorageNoInstance, Move) {
using value_type = typename TestFixture::type;
entt::storage<value_type> pool;
const std::array entity{entt::entity{3}, entt::entity{2}};
pool.emplace(entity[0u]);
static_assert(std::is_move_constructible_v<decltype(pool)>, "Move constructible type required");
static_assert(std::is_move_assignable_v<decltype(pool)>, "Move assignable type required");
entt::storage<value_type> other{std::move(pool)};
test::is_initialized(pool);
ASSERT_TRUE(pool.empty());
ASSERT_FALSE(other.empty());
ASSERT_EQ(other.type(), entt::type_id<value_type>());
ASSERT_EQ(other.index(entity[0u]), 0u);
entt::storage<value_type> extended{std::move(other), std::allocator<value_type>{}};
test::is_initialized(other);
ASSERT_TRUE(other.empty());
ASSERT_FALSE(extended.empty());
ASSERT_EQ(extended.type(), entt::type_id<value_type>());
ASSERT_EQ(extended.index(entity[0u]), 0u);
pool = std::move(extended);
test::is_initialized(extended);
ASSERT_FALSE(pool.empty());
ASSERT_TRUE(other.empty());
ASSERT_TRUE(extended.empty());
ASSERT_EQ(pool.type(), entt::type_id<value_type>());
ASSERT_EQ(pool.index(entity[0u]), 0u);
other = entt::storage<value_type>{};
other.emplace(entity[1u], 2);
other = std::move(pool);
test::is_initialized(pool);
ASSERT_FALSE(pool.empty());
ASSERT_FALSE(other.empty());
ASSERT_EQ(other.type(), entt::type_id<value_type>());
ASSERT_EQ(other.index(entity[0u]), 0u);
}
TYPED_TEST(StorageNoInstance, Swap) {
using value_type = typename TestFixture::type;
entt::storage<value_type> pool;
entt::storage<value_type> other;
ASSERT_EQ(pool.type(), entt::type_id<value_type>());
ASSERT_EQ(other.type(), entt::type_id<value_type>());
pool.emplace(entt::entity{4});
other.emplace(entt::entity{2});
other.emplace(entt::entity{1});
other.erase(entt::entity{2});
ASSERT_EQ(pool.size(), 1u);
ASSERT_EQ(other.size(), 1u);
pool.swap(other);
ASSERT_EQ(pool.type(), entt::type_id<value_type>());
ASSERT_EQ(other.type(), entt::type_id<value_type>());
ASSERT_EQ(pool.size(), 1u);
ASSERT_EQ(other.size(), 1u);
ASSERT_EQ(pool.index(entt::entity{1}), 0u);
ASSERT_EQ(other.index(entt::entity{4}), 0u);
}
TYPED_TEST(StorageNoInstance, Getters) {
using value_type = typename TestFixture::type;
entt::storage<value_type> pool;
const entt::entity entity{4};
pool.emplace(entity, 3);
testing::StaticAssertTypeEq<decltype(pool.get({})), void>();
testing::StaticAssertTypeEq<decltype(std::as_const(pool).get({})), void>();
testing::StaticAssertTypeEq<decltype(pool.get_as_tuple({})), std::tuple<>>();
testing::StaticAssertTypeEq<decltype(std::as_const(pool).get_as_tuple({})), std::tuple<>>();
ASSERT_NO_THROW(pool.get(entity));
ASSERT_NO_THROW(std::as_const(pool).get(entity));
ASSERT_EQ(pool.get_as_tuple(entity), std::make_tuple());
ASSERT_EQ(std::as_const(pool).get_as_tuple(entity), std::make_tuple());
}
ENTT_DEBUG_TYPED_TEST(StorageNoInstanceDeathTest, Getters) {
using value_type = typename TestFixture::type;
entt::storage<value_type> pool;
const entt::entity entity{4};
ASSERT_DEATH(pool.get(entity), "");
ASSERT_DEATH(std::as_const(pool).get(entity), "");
ASSERT_DEATH([[maybe_unused]] const auto value = pool.get_as_tuple(entity), "");
ASSERT_DEATH([[maybe_unused]] const auto value = std::as_const(pool).get_as_tuple(entity), "");
}
TYPED_TEST(StorageNoInstance, Value) {
using value_type = typename TestFixture::type;
entt::storage<value_type> pool;
const entt::entity entity{4};
pool.emplace(entity);
ASSERT_EQ(pool.value(entt::entity{4}), nullptr);
}
ENTT_DEBUG_TYPED_TEST(StorageNoInstanceDeathTest, Value) {
using value_type = typename TestFixture::type;
entt::storage<value_type> pool;
ASSERT_DEATH([[maybe_unused]] const void *value = pool.value(entt::entity{4}), "");
}
TYPED_TEST(StorageNoInstance, Emplace) {
using value_type = typename TestFixture::type;
entt::storage<value_type> pool;
const std::array entity{entt::entity{1}, entt::entity{3}};
testing::StaticAssertTypeEq<decltype(pool.emplace({})), void>();
ASSERT_NO_THROW(pool.emplace(entity[0u]));
ASSERT_NO_THROW(this->emplace_instance(pool, entity[1u]));
}
ENTT_DEBUG_TYPED_TEST(StorageNoInstanceDeathTest, Emplace) {
using value_type = typename TestFixture::type;
entt::storage<value_type> pool;
const entt::entity entity{4};
testing::StaticAssertTypeEq<decltype(pool.emplace({})), void>();
pool.emplace(entity);
ASSERT_DEATH(pool.emplace(entity), "");
ASSERT_DEATH(this->emplace_instance(pool, entity), "");
}
TYPED_TEST(StorageNoInstance, TryEmplace) {
using value_type = typename TestFixture::type;
entt::storage<value_type> pool;
entt::sparse_set &base = pool;
const std::array entity{entt::entity{1}, entt::entity{3}};
ASSERT_NE(this->push_instance(pool, entity[0u]), base.end());
ASSERT_EQ(pool.size(), 1u);
ASSERT_EQ(base.index(entity[0u]), 0u);
base.erase(entity[0u]);
ASSERT_NE(base.push(entity.begin(), entity.end()), base.end());
ASSERT_EQ(pool.size(), 2u);
ASSERT_EQ(base.index(entity[0u]), 0u);
ASSERT_EQ(base.index(entity[1u]), 1u);
base.erase(entity.begin(), entity.end());
ASSERT_NE(base.push(entity.rbegin(), entity.rend()), base.end());
ASSERT_EQ(pool.size(), 2u);
ASSERT_EQ(base.index(entity[0u]), 1u);
ASSERT_EQ(base.index(entity[1u]), 0u);
}
TYPED_TEST(StorageNoInstance, Patch) {
using value_type = typename TestFixture::type;
entt::storage<value_type> pool;
const entt::entity entity{4};
int counter = 0;
auto callback = [&counter]() { ++counter; };
pool.emplace(entity);
ASSERT_EQ(counter, 0);
pool.patch(entity);
pool.patch(entity, callback);
pool.patch(entity, callback, callback);
ASSERT_EQ(counter, 3);
}
ENTT_DEBUG_TYPED_TEST(StorageNoInstanceDeathTest, Patch) {
using value_type = typename TestFixture::type;
entt::storage<value_type> pool;
ASSERT_DEATH(pool.patch(entt::null), "");
}
TYPED_TEST(StorageNoInstance, Insert) {
using value_type = typename TestFixture::type;
entt::storage<value_type> pool;
const std::array entity{entt::entity{1}, entt::entity{3}};
pool.insert(entity.begin(), entity.end());
ASSERT_EQ(pool.size(), 2u);
ASSERT_EQ(pool.index(entity[0u]), 0u);
ASSERT_EQ(pool.index(entity[1u]), 1u);
pool.erase(entity.begin(), entity.end());
this->insert_instance(pool, entity.rbegin(), entity.rend());
ASSERT_EQ(pool.size(), 2u);
ASSERT_EQ(pool.index(entity[0u]), 1u);
ASSERT_EQ(pool.index(entity[1u]), 0u);
}
ENTT_DEBUG_TYPED_TEST(StorageNoInstanceDeathTest, Insert) {
using value_type = typename TestFixture::type;
entt::storage<value_type> pool;
const std::array entity{entt::entity{1}, entt::entity{3}};
pool.insert(entity.begin(), entity.end());
ASSERT_DEATH(pool.insert(entity.begin(), entity.end()), "");
ASSERT_DEATH(this->insert_instance(pool, entity.begin(), entity.end()), "");
}
TYPED_TEST(StorageNoInstance, Iterable) {
using value_type = typename TestFixture::type;
using iterator = typename entt::storage<value_type>::iterable::iterator;
testing::StaticAssertTypeEq<typename iterator::value_type, std::tuple<entt::entity>>();
testing::StaticAssertTypeEq<typename iterator::pointer, entt::input_iterator_pointer<std::tuple<entt::entity>>>();
testing::StaticAssertTypeEq<typename iterator::reference, typename iterator::value_type>();
entt::storage<value_type> pool;
const entt::sparse_set &base = pool;
pool.emplace(entt::entity{1});
pool.emplace(entt::entity{3});
auto iterable = pool.each();
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.base(), base.begin());
ASSERT_EQ(end.base(), base.end());
ASSERT_EQ(std::get<0>(*begin.operator->().operator->()), entt::entity{3});
ASSERT_EQ(std::get<0>(*begin), entt::entity{3});
ASSERT_EQ(begin++, iterable.begin());
ASSERT_EQ(begin.base(), ++base.begin());
ASSERT_EQ(++begin, iterable.end());
ASSERT_EQ(begin.base(), base.end());
for(auto [entity]: iterable) {
testing::StaticAssertTypeEq<decltype(entity), entt::entity>();
ASSERT_TRUE(entity == entt::entity{1} || entity == entt::entity{3});
}
}
TYPED_TEST(StorageNoInstance, ConstIterable) {
using value_type = typename TestFixture::type;
using iterator = typename entt::storage<value_type>::const_iterable::iterator;
testing::StaticAssertTypeEq<typename iterator::value_type, std::tuple<entt::entity>>();
testing::StaticAssertTypeEq<typename iterator::pointer, entt::input_iterator_pointer<std::tuple<entt::entity>>>();
testing::StaticAssertTypeEq<typename iterator::reference, typename iterator::value_type>();
entt::storage<value_type> pool;
const entt::sparse_set &base = pool;
pool.emplace(entt::entity{1});
pool.emplace(entt::entity{3});
auto iterable = std::as_const(pool).each();
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.base(), base.begin());
ASSERT_EQ(end.base(), base.end());
ASSERT_EQ(std::get<0>(*begin.operator->().operator->()), entt::entity{3});
ASSERT_EQ(std::get<0>(*begin), entt::entity{3});
ASSERT_EQ(begin++, iterable.begin());
ASSERT_EQ(begin.base(), ++base.begin());
ASSERT_EQ(++begin, iterable.end());
ASSERT_EQ(begin.base(), base.end());
for(auto [entity]: iterable) {
testing::StaticAssertTypeEq<decltype(entity), entt::entity>();
ASSERT_TRUE(entity == entt::entity{1} || entity == entt::entity{3});
}
}
TYPED_TEST(StorageNoInstance, IterableIteratorConversion) {
using value_type = typename TestFixture::type;
entt::storage<value_type> pool;
pool.emplace(entt::entity{3});
const typename entt::storage<value_type>::iterable::iterator it = pool.each().begin();
typename entt::storage<value_type>::const_iterable::iterator cit = it;
testing::StaticAssertTypeEq<decltype(*it), std::tuple<entt::entity>>();
testing::StaticAssertTypeEq<decltype(*cit), std::tuple<entt::entity>>();
ASSERT_EQ(it, cit);
ASSERT_NE(++cit, it);
}
TYPED_TEST(StorageNoInstance, IterableAlgorithmCompatibility) {
using value_type = typename TestFixture::type;
entt::storage<value_type> pool;
const entt::entity entity{3};
pool.emplace(entity);
const auto iterable = pool.each();
const auto it = std::find_if(iterable.begin(), iterable.end(), [entity](auto args) { return std::get<0>(args) == entity; });
ASSERT_EQ(std::get<0>(*it), entity);
}
TYPED_TEST(StorageNoInstance, ReverseIterable) {
using value_type = typename TestFixture::type;
using iterator = typename entt::storage<value_type>::reverse_iterable::iterator;
testing::StaticAssertTypeEq<typename iterator::value_type, std::tuple<entt::entity>>();
testing::StaticAssertTypeEq<typename iterator::pointer, entt::input_iterator_pointer<std::tuple<entt::entity>>>();
testing::StaticAssertTypeEq<typename iterator::reference, typename iterator::value_type>();
entt::storage<value_type> pool;
const entt::sparse_set &base = pool;
pool.emplace(entt::entity{1});
pool.emplace(entt::entity{3});
auto iterable = pool.reach();
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.base(), base.rbegin());
ASSERT_EQ(end.base(), base.rend());
ASSERT_EQ(std::get<0>(*begin.operator->().operator->()), entt::entity{1});
ASSERT_EQ(std::get<0>(*begin), entt::entity{1});
ASSERT_EQ(begin++, iterable.begin());
ASSERT_EQ(begin.base(), ++base.rbegin());
ASSERT_EQ(++begin, iterable.end());
ASSERT_EQ(begin.base(), base.rend());
for(auto [entity]: iterable) {
testing::StaticAssertTypeEq<decltype(entity), entt::entity>();
ASSERT_TRUE(entity == entt::entity{1} || entity == entt::entity{3});
}
}
TYPED_TEST(StorageNoInstance, ConstReverseIterable) {
using value_type = typename TestFixture::type;
using iterator = typename entt::storage<value_type>::const_reverse_iterable::iterator;
testing::StaticAssertTypeEq<typename iterator::value_type, std::tuple<entt::entity>>();
testing::StaticAssertTypeEq<typename iterator::pointer, entt::input_iterator_pointer<std::tuple<entt::entity>>>();
testing::StaticAssertTypeEq<typename iterator::reference, typename iterator::value_type>();
entt::storage<value_type> pool;
const entt::sparse_set &base = pool;
pool.emplace(entt::entity{1});
pool.emplace(entt::entity{3});
auto iterable = std::as_const(pool).reach();
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.base(), base.rbegin());
ASSERT_EQ(end.base(), base.rend());
ASSERT_EQ(std::get<0>(*begin.operator->().operator->()), entt::entity{1});
ASSERT_EQ(std::get<0>(*begin), entt::entity{1});
ASSERT_EQ(begin++, iterable.begin());
ASSERT_EQ(begin.base(), ++base.rbegin());
ASSERT_EQ(++begin, iterable.end());
ASSERT_EQ(begin.base(), base.rend());
for(auto [entity]: iterable) {
testing::StaticAssertTypeEq<decltype(entity), entt::entity>();
ASSERT_TRUE(entity == entt::entity{1} || entity == entt::entity{3});
}
}
TYPED_TEST(StorageNoInstance, ReverseIterableIteratorConversion) {
using value_type = typename TestFixture::type;
entt::storage<value_type> pool;
pool.emplace(entt::entity{3});
const typename entt::storage<value_type>::reverse_iterable::iterator it = pool.reach().begin();
typename entt::storage<value_type>::const_reverse_iterable::iterator cit = it;
testing::StaticAssertTypeEq<decltype(*it), std::tuple<entt::entity>>();
testing::StaticAssertTypeEq<decltype(*cit), std::tuple<entt::entity>>();
ASSERT_EQ(it, cit);
ASSERT_NE(++cit, it);
}
TYPED_TEST(StorageNoInstance, ReverseIterableAlgorithmCompatibility) {
using value_type = typename TestFixture::type;
entt::storage<value_type> pool;
const entt::entity entity{3};
pool.emplace(entity);
const auto iterable = pool.reach();
const auto it = std::find_if(iterable.begin(), iterable.end(), [entity](auto args) { return std::get<0>(args) == entity; });
ASSERT_EQ(std::get<0>(*it), entity);
}
TYPED_TEST(StorageNoInstance, SortOrdered) {
using value_type = typename TestFixture::type;
entt::storage<value_type> pool;
const std::array entity{entt::entity{16}, entt::entity{8}, entt::entity{4}, entt::entity{2}, entt::entity{1}};
pool.insert(entity.begin(), entity.end());
pool.sort(std::less{});
ASSERT_TRUE(std::equal(entity.rbegin(), entity.rend(), pool.begin(), pool.end()));
}
TYPED_TEST(StorageNoInstance, SortReverse) {
using value_type = typename TestFixture::type;
entt::storage<value_type> pool;
const std::array entity{entt::entity{1}, entt::entity{2}, entt::entity{4}, entt::entity{8}, entt::entity{16}};
pool.insert(entity.begin(), entity.end());
pool.sort(std::less{});
ASSERT_TRUE(std::equal(entity.begin(), entity.end(), pool.begin(), pool.end()));
}
TYPED_TEST(StorageNoInstance, SortUnordered) {
using value_type = typename TestFixture::type;
entt::storage<value_type> pool;
const std::array entity{entt::entity{4}, entt::entity{2}, entt::entity{1}, entt::entity{8}, entt::entity{16}};
pool.insert(entity.begin(), entity.end());
pool.sort(std::less{});
ASSERT_EQ(pool.data()[0u], entity[4u]);
ASSERT_EQ(pool.data()[1u], entity[3u]);
ASSERT_EQ(pool.data()[2u], entity[0u]);
ASSERT_EQ(pool.data()[3u], entity[1u]);
ASSERT_EQ(pool.data()[4u], entity[2u]);
}
TYPED_TEST(StorageNoInstance, SortN) {
using value_type = typename TestFixture::type;
entt::storage<value_type> pool;
const std::array entity{entt::entity{2}, entt::entity{4}, entt::entity{1}, entt::entity{8}, entt::entity{16}};
pool.insert(entity.begin(), entity.end());
pool.sort_n(0u, std::less{});
ASSERT_TRUE(std::equal(entity.rbegin(), entity.rend(), pool.begin(), pool.end()));
pool.sort_n(2u, std::less{});
ASSERT_EQ(pool.data()[0u], entity[1u]);
ASSERT_EQ(pool.data()[1u], entity[0u]);
ASSERT_EQ(pool.data()[2u], entity[2u]);
const auto length = 5u;
pool.sort_n(length, std::less{});
ASSERT_EQ(pool.data()[0u], entity[4u]);
ASSERT_EQ(pool.data()[1u], entity[3u]);
ASSERT_EQ(pool.data()[2u], entity[1u]);
ASSERT_EQ(pool.data()[3u], entity[0u]);
ASSERT_EQ(pool.data()[4u], entity[2u]);
}
TYPED_TEST(StorageNoInstance, SortAsDisjoint) {
using value_type = typename TestFixture::type;
entt::storage<value_type> lhs;
const entt::storage<value_type> rhs;
const std::array entity{entt::entity{1}, entt::entity{2}, entt::entity{4}};
lhs.insert(entity.begin(), entity.end());
ASSERT_TRUE(std::equal(entity.rbegin(), entity.rend(), lhs.begin(), lhs.end()));
lhs.sort_as(rhs.begin(), rhs.end());
ASSERT_TRUE(std::equal(entity.rbegin(), entity.rend(), lhs.begin(), lhs.end()));
}
TYPED_TEST(StorageNoInstance, SortAsOverlap) {
using value_type = typename TestFixture::type;
entt::storage<value_type> lhs;
entt::storage<value_type> rhs;
const std::array lhs_entity{entt::entity{1}, entt::entity{2}, entt::entity{4}};
const std::array rhs_entity{entt::entity{2}};
lhs.insert(lhs_entity.begin(), lhs_entity.end());
rhs.insert(rhs_entity.begin(), rhs_entity.end());
ASSERT_TRUE(std::equal(lhs_entity.rbegin(), lhs_entity.rend(), lhs.begin(), lhs.end()));
ASSERT_TRUE(std::equal(rhs_entity.rbegin(), rhs_entity.rend(), rhs.begin(), rhs.end()));
lhs.sort_as(rhs.begin(), rhs.end());
ASSERT_EQ(lhs.data()[0u], lhs_entity[0u]);
ASSERT_EQ(lhs.data()[1u], lhs_entity[2u]);
ASSERT_EQ(lhs.data()[2u], lhs_entity[1u]);
}
TYPED_TEST(StorageNoInstance, SortAsOrdered) {
using value_type = typename TestFixture::type;
entt::storage<value_type> lhs;
entt::storage<value_type> rhs;
const std::array lhs_entity{entt::entity{1}, entt::entity{2}, entt::entity{4}, entt::entity{8}, entt::entity{16}};
const std::array rhs_entity{entt::entity{32}, entt::entity{1}, entt::entity{2}, entt::entity{4}, entt::entity{8}, entt::entity{16}};
lhs.insert(lhs_entity.begin(), lhs_entity.end());
rhs.insert(rhs_entity.begin(), rhs_entity.end());
ASSERT_TRUE(std::equal(lhs_entity.rbegin(), lhs_entity.rend(), lhs.begin(), lhs.end()));
ASSERT_TRUE(std::equal(rhs_entity.rbegin(), rhs_entity.rend(), rhs.begin(), rhs.end()));
rhs.sort_as(lhs.begin(), lhs.end());
ASSERT_TRUE(std::equal(rhs_entity.rbegin(), rhs_entity.rend(), rhs.begin(), rhs.end()));
}
TYPED_TEST(StorageNoInstance, SortAsReverse) {
using value_type = typename TestFixture::type;
entt::storage<value_type> lhs;
entt::storage<value_type> rhs;
const std::array lhs_entity{entt::entity{1}, entt::entity{2}, entt::entity{4}, entt::entity{8}, entt::entity{16}};
const std::array rhs_entity{entt::entity{16}, entt::entity{8}, entt::entity{4}, entt::entity{2}, entt::entity{1}, entt::entity{32}};
lhs.insert(lhs_entity.begin(), lhs_entity.end());
rhs.insert(rhs_entity.begin(), rhs_entity.end());
ASSERT_TRUE(std::equal(lhs_entity.rbegin(), lhs_entity.rend(), lhs.begin(), lhs.end()));
ASSERT_TRUE(std::equal(rhs_entity.rbegin(), rhs_entity.rend(), rhs.begin(), rhs.end()));
rhs.sort_as(lhs.begin(), lhs.end());
ASSERT_EQ(rhs.data()[0u], rhs_entity[5u]);
ASSERT_EQ(rhs.data()[1u], rhs_entity[4u]);
ASSERT_EQ(rhs.data()[2u], rhs_entity[3u]);
ASSERT_EQ(rhs.data()[3u], rhs_entity[2u]);
ASSERT_EQ(rhs.data()[4u], rhs_entity[1u]);
ASSERT_EQ(rhs.data()[5u], rhs_entity[0u]);
}
TYPED_TEST(StorageNoInstance, SortAsUnordered) {
using value_type = typename TestFixture::type;
entt::storage<value_type> lhs;
entt::storage<value_type> rhs;
const std::array lhs_entity{entt::entity{1}, entt::entity{2}, entt::entity{4}, entt::entity{8}, entt::entity{16}};
const std::array rhs_entity{entt::entity{4}, entt::entity{2}, entt::entity{32}, entt::entity{1}, entt::entity{8}, entt::entity{16}};
lhs.insert(lhs_entity.begin(), lhs_entity.end());
rhs.insert(rhs_entity.begin(), rhs_entity.end());
ASSERT_TRUE(std::equal(lhs_entity.rbegin(), lhs_entity.rend(), lhs.begin(), lhs.end()));
ASSERT_TRUE(std::equal(rhs_entity.rbegin(), rhs_entity.rend(), rhs.begin(), rhs.end()));
rhs.sort_as(lhs.begin(), lhs.end());
ASSERT_EQ(rhs.data()[0u], rhs_entity[2u]);
ASSERT_EQ(rhs.data()[1u], rhs_entity[3u]);
ASSERT_EQ(rhs.data()[2u], rhs_entity[1u]);
ASSERT_EQ(rhs.data()[3u], rhs_entity[0u]);
ASSERT_EQ(rhs.data()[4u], rhs_entity[4u]);
ASSERT_EQ(rhs.data()[5u], rhs_entity[5u]);
}

View File

@@ -0,0 +1,41 @@
#include <gtest/gtest.h>
#include <entt/entity/storage.hpp>
template<typename Type>
struct StorageUtility: testing::Test {
using type = Type;
};
using StorageUtilityTypes = ::testing::Types<int, char, double, void>;
TYPED_TEST_SUITE(StorageUtility, StorageUtilityTypes, );
TYPED_TEST(StorageUtility, StorageType) {
using value_type = typename TestFixture::type;
// just a bunch of static asserts to avoid regressions
#ifdef ENTT_NO_MIXIN
testing::StaticAssertTypeEq<entt::storage_type_t<value_type, entt::entity>, entt::basic_storage<value_type, entt::entity>>();
testing::StaticAssertTypeEq<entt::storage_type_t<value_type>, entt::storage<value_type>>();
#else
testing::StaticAssertTypeEq<entt::storage_type_t<value_type, entt::entity>, entt::sigh_mixin<entt::basic_storage<value_type, entt::entity>>>();
testing::StaticAssertTypeEq<entt::storage_type_t<value_type>, entt::sigh_mixin<entt::storage<value_type>>>();
#endif // ENTT_NO_MIXIN
}
TYPED_TEST(StorageUtility, StorageFor) {
using value_type = typename TestFixture::type;
// just a bunch of static asserts to avoid regressions
#ifdef ENTT_NO_MIXIN
testing::StaticAssertTypeEq<entt::storage_for_t<const value_type, entt::entity>, const entt::basic_storage<value_type, entt::entity>>();
testing::StaticAssertTypeEq<entt::storage_for_t<value_type, entt::entity>, entt::basic_storage<value_type, entt::entity>>();
testing::StaticAssertTypeEq<entt::storage_for_t<const value_type>, const entt::storage<value_type>>();
testing::StaticAssertTypeEq<entt::storage_for_t<value_type>, entt::storage<value_type>>();
#else
testing::StaticAssertTypeEq<entt::storage_for_t<const value_type, entt::entity>, const entt::sigh_mixin<entt::basic_storage<value_type, entt::entity>>>();
testing::StaticAssertTypeEq<entt::storage_for_t<value_type, entt::entity>, entt::sigh_mixin<entt::basic_storage<value_type, entt::entity>>>();
testing::StaticAssertTypeEq<entt::storage_for_t<const value_type>, const entt::sigh_mixin<entt::storage<value_type>>>();
testing::StaticAssertTypeEq<entt::storage_for_t<value_type>, entt::sigh_mixin<entt::storage<value_type>>>();
#endif
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,574 @@
#include <cstddef>
#include <memory>
#include <utility>
#include <gtest/gtest.h>
#include <entt/graph/adjacency_matrix.hpp>
#include "../../common/linter.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_TRUE(adjacency_matrix.empty());
ASSERT_EQ(adjacency_matrix.size(), 3u);
adjacency_matrix.insert(0u, 1u);
ASSERT_FALSE(adjacency_matrix.empty());
const entt::adjacency_matrix<entt::directed_tag> temp{adjacency_matrix, adjacency_matrix.get_allocator()};
const entt::adjacency_matrix<entt::directed_tag> other{std::move(adjacency_matrix), adjacency_matrix.get_allocator()};
test::is_initialized(adjacency_matrix);
ASSERT_TRUE(adjacency_matrix.empty());
ASSERT_FALSE(other.empty());
ASSERT_EQ(other.size(), 3u);
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_FALSE(adjacency_matrix.empty());
ASSERT_TRUE(adjacency_matrix.contains(0u, 1u));
ASSERT_EQ(other.size(), 3u);
ASSERT_TRUE(other.contains(0u, 1u));
adjacency_matrix.resize(4u);
adjacency_matrix.insert(0u, 2u);
other.insert(1u, 2u);
other = adjacency_matrix;
ASSERT_FALSE(adjacency_matrix.empty());
ASSERT_EQ(adjacency_matrix.size(), 4u);
ASSERT_EQ(other.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)};
test::is_initialized(adjacency_matrix);
ASSERT_TRUE(adjacency_matrix.empty());
ASSERT_EQ(other.size(), 3u);
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);
test::is_initialized(adjacency_matrix);
ASSERT_TRUE(adjacency_matrix.empty());
ASSERT_EQ(other.size(), 4u);
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));
ASSERT_FALSE(adjacency_matrix.empty());
ASSERT_TRUE(other.empty());
adjacency_matrix.swap(other);
ASSERT_TRUE(adjacency_matrix.empty());
ASSERT_FALSE(other.empty());
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_FALSE(adjacency_matrix.empty());
ASSERT_TRUE(adjacency_matrix.contains(0u, 1u));
ASSERT_TRUE(adjacency_matrix.contains(0u, 2u));
ASSERT_EQ(adjacency_matrix.size(), 3u);
adjacency_matrix.clear();
ASSERT_TRUE(adjacency_matrix.empty());
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;
testing::StaticAssertTypeEq<iterator::value_type, std::size_t>();
testing::StaticAssertTypeEq<iterator::pointer, void>();
testing::StaticAssertTypeEq<iterator::reference, std::size_t>();
const 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;
testing::StaticAssertTypeEq<iterator::value_type, std::pair<std::size_t, std::size_t>>();
testing::StaticAssertTypeEq<iterator::pointer, entt::input_iterator_pointer<std::pair<std::size_t, std::size_t>>>();
testing::StaticAssertTypeEq<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, EdgesBackwardOnlyDirected) {
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{2u};
auto iterable = adjacency_matrix.edges();
ASSERT_EQ(iterable.begin(), iterable.end());
adjacency_matrix.insert(1u, 0u);
iterable = adjacency_matrix.edges();
ASSERT_NE(iterable.begin(), iterable.end());
auto it = iterable.begin();
ASSERT_EQ(*it++, std::make_pair(std::size_t{1u}, std::size_t{0u}));
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, EdgesBackwardOnlyUndirected) {
entt::adjacency_matrix<entt::undirected_tag> adjacency_matrix{2u};
auto iterable = adjacency_matrix.edges();
ASSERT_EQ(iterable.begin(), iterable.end());
adjacency_matrix.insert(1u, 0u);
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++, std::make_pair(std::size_t{1u}, std::size_t{0u}));
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, OutEdgesBackwardOnlyDirected) {
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{2u};
auto iterable = adjacency_matrix.out_edges(1u);
ASSERT_EQ(iterable.begin(), iterable.end());
adjacency_matrix.insert(1u, 0u);
iterable = adjacency_matrix.out_edges(1u);
ASSERT_NE(iterable.begin(), iterable.end());
auto it = iterable.begin();
ASSERT_EQ(*it++, std::make_pair(std::size_t{1u}, std::size_t{0u}));
ASSERT_EQ(it, iterable.end());
iterable = adjacency_matrix.out_edges(0u);
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, OutEdgesBackwardOnlyUndirected) {
entt::adjacency_matrix<entt::undirected_tag> adjacency_matrix{2u};
auto iterable = adjacency_matrix.out_edges(1u);
ASSERT_EQ(iterable.begin(), iterable.end());
adjacency_matrix.insert(1u, 0u);
iterable = adjacency_matrix.out_edges(1u);
ASSERT_NE(iterable.begin(), iterable.end());
auto it = iterable.begin();
ASSERT_EQ(*it++, std::make_pair(std::size_t{1u}, std::size_t{0u}));
ASSERT_EQ(it, iterable.end());
iterable = adjacency_matrix.out_edges(0u);
it = iterable.cbegin();
ASSERT_NE(it, iterable.cend());
ASSERT_EQ(*it++, std::make_pair(std::size_t{0u}, 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, InEdgesBackwardOnlyDirected) {
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{2u};
auto iterable = adjacency_matrix.in_edges(0u);
ASSERT_EQ(iterable.begin(), iterable.end());
adjacency_matrix.insert(1u, 0u);
iterable = adjacency_matrix.in_edges(0u);
ASSERT_NE(iterable.begin(), iterable.end());
auto it = iterable.begin();
ASSERT_EQ(*it++, std::make_pair(std::size_t{1u}, std::size_t{0u}));
ASSERT_EQ(it, iterable.end());
iterable = adjacency_matrix.in_edges(1u);
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++, std::make_pair(std::size_t{2u}, 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, InEdgesBackwardOnlyUndirected) {
entt::adjacency_matrix<entt::undirected_tag> adjacency_matrix{2u};
auto iterable = adjacency_matrix.in_edges(0u);
ASSERT_EQ(iterable.begin(), iterable.end());
adjacency_matrix.insert(1u, 0u);
iterable = adjacency_matrix.in_edges(0u);
ASSERT_NE(iterable.begin(), iterable.end());
auto it = iterable.begin();
ASSERT_EQ(*it++, std::make_pair(std::size_t{1u}, std::size_t{0u}));
ASSERT_EQ(it, iterable.end());
iterable = adjacency_matrix.in_edges(1u);
it = iterable.cbegin();
ASSERT_NE(it, iterable.cend());
ASSERT_EQ(*it++, std::make_pair(std::size_t{0u}, std::size_t{1u}));
ASSERT_EQ(it, iterable.cend());
}
TEST(AdjacencyMatrix, ThrowingAllocator) {
entt::adjacency_matrix<entt::directed_tag, test::throwing_allocator<std::size_t>> adjacency_matrix{2u};
adjacency_matrix.insert(0u, 1u);
adjacency_matrix.get_allocator().throw_counter<std::size_t>(0u);
ASSERT_EQ(adjacency_matrix.size(), 2u);
ASSERT_TRUE(adjacency_matrix.contains(0u, 1u));
ASSERT_THROW(adjacency_matrix.resize(4u), test::throwing_allocator_exception);
ASSERT_EQ(adjacency_matrix.size(), 2u);
ASSERT_TRUE(adjacency_matrix.contains(0u, 1u));
}

View File

@@ -0,0 +1,63 @@
#include <cstddef>
#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 = R"(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);
}

View File

@@ -0,0 +1,350 @@
#include <array>
#include <cstddef>
#include <memory>
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/graph/flow.hpp>
#include "../../common/config.h"
#include "../../common/linter.hpp"
#include "../../common/throwing_allocator.hpp"
TEST(Flow, Constructors) {
entt::flow flow{};
ASSERT_TRUE(flow.empty());
ASSERT_EQ(flow.size(), 0u);
flow = entt::flow{std::allocator<entt::id_type>{}};
ASSERT_TRUE(flow.empty());
ASSERT_EQ(flow.size(), 0u);
flow.bind(2);
flow.bind(4);
flow.bind(8);
ASSERT_FALSE(flow.empty());
ASSERT_EQ(flow.size(), 3u);
const entt::flow temp{flow, flow.get_allocator()};
const entt::flow other{std::move(flow), flow.get_allocator()};
test::is_initialized(flow);
ASSERT_TRUE(flow.empty());
ASSERT_EQ(other.size(), 3u);
ASSERT_EQ(other[0u], 2);
ASSERT_EQ(other[1u], 4);
ASSERT_EQ(other[2u], 8);
}
TEST(Flow, Copy) {
entt::flow flow{};
flow.bind(2);
flow.bind(4);
flow.bind(8);
entt::flow other{flow};
ASSERT_EQ(flow.size(), 3u);
ASSERT_EQ(other.size(), 3u);
ASSERT_EQ(other[0u], 2);
ASSERT_EQ(other[1u], 4);
ASSERT_EQ(other[2u], 8);
flow.bind(1);
other.bind(3);
other = flow;
ASSERT_EQ(other.size(), 4u);
ASSERT_EQ(flow.size(), 4u);
ASSERT_EQ(other[0u], 2);
ASSERT_EQ(other[1u], 4);
ASSERT_EQ(other[2u], 8);
ASSERT_EQ(other[3u], 1);
}
TEST(Flow, Move) {
entt::flow flow{};
flow.bind(2);
flow.bind(4);
flow.bind(8);
entt::flow other{std::move(flow)};
test::is_initialized(flow);
ASSERT_TRUE(flow.empty());
ASSERT_EQ(other.size(), 3u);
ASSERT_EQ(other[0u], 2);
ASSERT_EQ(other[1u], 4);
ASSERT_EQ(other[2u], 8);
flow = {};
flow.bind(1);
other.bind(3);
other = std::move(flow);
test::is_initialized(flow);
ASSERT_TRUE(flow.empty());
ASSERT_EQ(other.size(), 1u);
ASSERT_EQ(other[0u], 1);
}
TEST(Flow, Swap) {
entt::flow flow{};
entt::flow other{};
flow.bind(8);
ASSERT_EQ(other.size(), 0u);
ASSERT_EQ(flow.size(), 1u);
ASSERT_EQ(flow[0u], 8);
flow.swap(other);
ASSERT_EQ(other.size(), 1u);
ASSERT_EQ(flow.size(), 0u);
ASSERT_EQ(other[0u], 8);
}
TEST(Flow, Clear) {
entt::flow flow{};
flow.bind(0);
flow.bind(4);
ASSERT_EQ(flow.size(), 2u);
ASSERT_EQ(flow[0u], 0);
ASSERT_EQ(flow[1u], 4);
flow.clear();
ASSERT_EQ(flow.size(), 0u);
}
TEST(Flow, Set) {
entt::flow flow{};
flow.bind(0).set(2, true).bind(1).set(2, true).set(3, 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(2).bind(1).ro(2).ro(3);
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 std::array<entt::id_type, 2u> res{10, 11};
flow.bind(0).ro(res.begin(), res.begin() + 1u).bind(1).ro(res.begin(), res.end());
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(2).bind(1).rw(2).rw(3);
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 std::array<entt::id_type, 2u> res{10, 11};
flow.bind(0).rw(res.begin(), res.begin() + 1u).bind(1).rw(res.begin(), res.end());
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);
}
ENTT_DEBUG_TEST(FlowDeathTest, NoBind) {
entt::flow flow{};
ASSERT_DEATH(flow.ro(4), "");
ASSERT_DEATH(flow.rw(4), "");
flow.bind(0);
ASSERT_NO_THROW(flow.ro(1));
ASSERT_NO_THROW(flow.rw(2));
}
TEST(Flow, DirectRebind) {
entt::flow flow{};
flow.bind(0).ro(2).rw(2).bind(1).ro(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, DeferredRebind) {
entt::flow flow{};
flow.bind(0).ro(2).bind(1).ro(2).bind(0).rw(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_FALSE(graph.contains(0u, 1u));
ASSERT_TRUE(graph.contains(1u, 0u));
}
TEST(Flow, Loop) {
entt::flow flow{};
flow.bind(0).rw(2).bind(1).ro(2).bind(0).rw(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_TRUE(graph.contains(1u, 0u));
}
TEST(Flow, ThrowingAllocator) {
entt::basic_flow<test::throwing_allocator<entt::id_type>> flow{};
flow.get_allocator().throw_counter<std::pair<std::size_t, entt::id_type>>(0u);
ASSERT_EQ(flow.size(), 0u);
ASSERT_THROW(flow.bind(1), test::throwing_allocator_exception);
ASSERT_EQ(flow.size(), 0u);
flow.bind(1);
ASSERT_EQ(flow.size(), 1u);
}

View File

@@ -0,0 +1,90 @@
#include <memory>
#include <gtest/gtest.h>
#include <entt/locator/locator.hpp>
#include "../../common/config.h"
struct base_service {
virtual ~base_service() = default;
virtual int invoke(int) = 0;
};
struct derived_service: base_service {
derived_service(int val)
: value{val} {}
int invoke(int other) override {
return value + other;
}
private:
int value;
};
struct ServiceLocator: ::testing::Test {
void SetUp() override {
entt::locator<base_service>::reset();
}
};
using ServiceLocatorDeathTest = ServiceLocator;
TEST_F(ServiceLocator, ValueAndTheLike) {
ASSERT_FALSE(entt::locator<base_service>::has_value());
ASSERT_EQ(entt::locator<base_service>::value_or<derived_service>(1).invoke(3), 4);
ASSERT_TRUE(entt::locator<base_service>::has_value());
ASSERT_EQ(entt::locator<base_service>::value().invoke(9), 10);
}
TEST_F(ServiceLocator, Emplace) {
ASSERT_FALSE(entt::locator<base_service>::has_value());
ASSERT_EQ(entt::locator<base_service>::emplace<derived_service>(5).invoke(1), 6);
ASSERT_TRUE(entt::locator<base_service>::has_value());
ASSERT_EQ(entt::locator<base_service>::value().invoke(3), 8);
entt::locator<base_service>::reset();
ASSERT_FALSE(entt::locator<base_service>::has_value());
ASSERT_EQ(entt::locator<base_service>::emplace<derived_service>(std::allocator_arg, std::allocator<derived_service>{}, 5).invoke(1), 6);
ASSERT_TRUE(entt::locator<base_service>::has_value());
ASSERT_EQ(entt::locator<base_service>::value().invoke(3), 8);
}
TEST_F(ServiceLocator, ResetHandle) {
entt::locator<base_service>::emplace<derived_service>(1);
auto handle = entt::locator<base_service>::handle();
ASSERT_TRUE(entt::locator<base_service>::has_value());
ASSERT_EQ(entt::locator<base_service>::value().invoke(3), 4);
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());
ASSERT_EQ(entt::locator<base_service>::value().invoke(3), 4);
}
TEST_F(ServiceLocator, ElementWithDeleter) {
derived_service service{1};
entt::locator<base_service>::reset(&service, [&service](base_service *serv) {
ASSERT_EQ(serv, &service);
service = derived_service{2};
});
ASSERT_TRUE(entt::locator<base_service>::has_value());
ASSERT_EQ(entt::locator<base_service>::value().invoke(1), 2);
entt::locator<base_service>::reset();
ASSERT_EQ(service.invoke(1), 3);
}
ENTT_DEBUG_TEST_F(ServiceLocatorDeathTest, UninitializedValue) {
ASSERT_EQ(entt::locator<base_service>::value_or<derived_service>(1).invoke(1), 2);
entt::locator<base_service>::reset();
ASSERT_DEATH(entt::locator<base_service>::value().invoke(4), "");
}

View File

@@ -0,0 +1,35 @@
load("@entt//bazel:copts.bzl", "COPTS")
load("@rules_cc//cc:defs.bzl", "cc_test")
# buildifier: keep sorted
_TESTS = [
"meta_any",
"meta_base",
"meta_container",
"meta_context",
"meta_conv",
"meta_ctor",
"meta_custom",
"meta_data",
"meta_dtor",
"meta_factory",
"meta_func",
"meta_handle",
"meta_pointer",
"meta_range",
"meta_template",
"meta_type",
"meta_utility",
]
[cc_test(
name = test,
srcs = ["{}.cpp".format(test)],
copts = COPTS,
deps = [
"//common",
"@entt",
"@googletest//:gtest",
"@googletest//:gtest_main",
],
) for test in _TESTS]

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,194 @@
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/locator/locator.hpp>
#include <entt/meta/context.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/node.hpp>
#include <entt/meta/resolve.hpp>
struct base_1 {
base_1() = default;
int value_1{};
};
struct base_2 {
base_2() = default;
operator int() const {
return value_2;
}
int value_2{};
};
struct base_3: base_2 {
base_3() = default;
int value_3{};
};
struct derived: base_1, base_3 {
derived() = default;
int value{};
};
struct MetaBase: ::testing::Test {
void SetUp() override {
using namespace entt::literals;
entt::meta_factory<base_1>{}
.data<&base_1::value_1>("value_1"_hs);
entt::meta_factory<base_2>{}
.conv<int>()
.data<&base_2::value_2>("value_2"_hs);
entt::meta_factory<base_3>{}
.base<base_2>()
.data<&base_3::value_3>("value_3"_hs);
entt::meta_factory<derived>{}
.type("derived"_hs)
.base<base_1>()
.base<base_3>()
.data<&derived::value>("value"_hs);
}
void TearDown() override {
entt::meta_reset();
}
};
TEST_F(MetaBase, Base) {
auto any = entt::resolve<derived>().construct();
any.cast<derived &>().value_1 = 2;
auto as_derived = any.as_ref();
ASSERT_TRUE(any.allow_cast<base_1 &>());
ASSERT_FALSE(any.allow_cast<char>());
ASSERT_FALSE(as_derived.allow_cast<char>());
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<base_1 &>().value_1, as_derived.cast<derived &>().value_1);
any.cast<base_1 &>().value_1 = 3;
ASSERT_EQ(any.cast<const base_1 &>().value_1, as_derived.cast<const derived &>().value_1);
}
TEST_F(MetaBase, SetGetWithMutatingThis) {
using namespace entt::literals;
derived 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 *>(&instance)), static_cast<const void *>(static_cast<const base_2 *>(&instance)));
ASSERT_NE(static_cast<const void *>(static_cast<const base_1 *>(&instance)), static_cast<const void *>(static_cast<const base_3 *>(&instance)));
ASSERT_EQ(static_cast<const void *>(static_cast<const base_2 *>(&instance)), static_cast<const void *>(static_cast<const base_3 *>(&instance)));
ASSERT_EQ(static_cast<const void *>(&instance), static_cast<const void *>(static_cast<const base_1 *>(&instance)));
ASSERT_TRUE(any.set("value"_hs, 0));
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, 4));
ASSERT_FALSE(as_cref.set("value_1"_hs, 4));
ASSERT_FALSE(as_cref.set("value_2"_hs, 4));
ASSERT_FALSE(as_cref.set("value_3"_hs, 4));
ASSERT_EQ(any.get("value"_hs).cast<int>(), 0);
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>(), 0);
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, 0);
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{}};
auto &&ref = any.cast<derived &>();
auto as_cref = std::as_const(any).as_ref();
ref.value_2 = 2;
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>(), 2);
ASSERT_EQ(from_cref.cast<int>(), 2);
ASSERT_TRUE(as_cref.allow_cast<int>());
ASSERT_TRUE(any.allow_cast<int>());
ASSERT_EQ(as_cref.cast<int>(), 2);
ASSERT_EQ(any.cast<int>(), 2);
}
TEST_F(MetaBase, OpaqueConvWithMutatingThis) {
entt::meta_any any{derived{}};
auto as_cref = std::as_const(any).as_ref();
any.cast<derived &>().value_2 = 2;
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>(), 2);
ASSERT_EQ(from_cref.cast<int>(), 2);
ASSERT_TRUE(as_cref.allow_cast(entt::resolve<int>()));
ASSERT_TRUE(any.allow_cast(entt::resolve<int>()));
ASSERT_EQ(as_cref.cast<int>(), 2);
ASSERT_EQ(any.cast<int>(), 2);
}
TEST_F(MetaBase, AssignWithMutatingThis) {
using namespace entt::literals;
entt::meta_any dst{base_2{}};
entt::meta_any src{derived{}};
dst.cast<base_2 &>().value_2 = 0;
src.cast<derived &>().value_2 = 1;
ASSERT_TRUE(dst.assign(src));
ASSERT_EQ(dst.get("value_2"_hs).cast<int>(), 1);
}
TEST_F(MetaBase, TransferWithMutatingThis) {
using namespace entt::literals;
entt::meta_any dst{base_2{}};
entt::meta_any src{derived{}};
dst.cast<base_2 &>().value_2 = 0;
src.cast<derived &>().value_2 = 1;
ASSERT_TRUE(dst.assign(std::move(src)));
ASSERT_EQ(dst.get("value_2"_hs).cast<int>(), 1);
}
TEST_F(MetaBase, ReRegistration) {
SetUp();
auto &&node = entt::internal::resolve<derived>(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,827 @@
#include <array>
#include <deque>
#include <list>
#include <map>
#include <set>
#include <utility>
#include <vector>
#include <gtest/gtest.h>
#include <entt/container/dense_map.hpp>
#include <entt/container/dense_set.hpp>
#include <entt/meta/container.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/resolve.hpp>
#include "../../common/config.h"
#include "../../common/empty.h"
#include "../../common/non_default_constructible.h"
TEST(MetaContainer, Invalid) {
ASSERT_FALSE(entt::meta_any{0}.as_sequence_container());
ASSERT_FALSE(entt::meta_any{0}.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(SequenceContainer, Empty) {
entt::meta_sequence_container container{};
ASSERT_FALSE(container);
entt::meta_any any{std::vector<int>{}};
container = any.as_sequence_container();
ASSERT_TRUE(container);
}
TEST(SequenceContainer, Iterator) {
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(SequenceContainer, 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, test::empty{}));
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(), 64);
ASSERT_TRUE(ret);
ASSERT_EQ(*ret, 64);
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_TRUE(view.reserve(8u));
ASSERT_EQ(view.size(), 0u);
}
TEST(SequenceContainer, 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, test::empty{}));
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_TRUE(view.reserve(8u));
ASSERT_EQ(cview.size(), 0u);
}
TEST(SequenceContainer, 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_FALSE(view.reserve(8u));
ASSERT_EQ(view.size(), 3u);
}
TEST(SequenceContainer, 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, test::empty{}));
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(), 64);
ASSERT_TRUE(ret);
ASSERT_EQ(*ret, 64);
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_FALSE(view.reserve(8u));
ASSERT_EQ(view.size(), 0u);
}
TEST(SequenceContainer, 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, test::empty{}));
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(), 64);
ASSERT_TRUE(ret);
ASSERT_EQ(*ret, 64);
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_FALSE(view.reserve(8u));
ASSERT_EQ(view.size(), 0u);
}
TEST(SequenceContainer, NonDefaultConstructible) {
std::vector<test::non_default_constructible> vec{};
auto any = entt::forward_as_meta(vec);
auto view = any.as_sequence_container();
ASSERT_FALSE(view.resize(5u));
}
TEST(SequenceContainer, Constness) {
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(64);
ASSERT_EQ(view.size(), 1u);
ASSERT_NE(view.begin(), view.end());
ASSERT_EQ(view[0].cast<const int &>(), 64);
auto it = view.begin();
auto ret = view.insert(it, 0);
ASSERT_FALSE(ret);
ASSERT_EQ(view.size(), 1u);
ASSERT_EQ(it->cast<int>(), 64);
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_FALSE(view.reserve(8u));
ASSERT_EQ(view.size(), 1u);
}
ENTT_DEBUG_TEST(SequenceContainerDeathTest, Constness) {
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(SequenceContainer, FromConstAny) {
const std::vector<int> vec{64};
const entt::meta_any any{vec};
auto view = any.as_sequence_container();
ASSERT_TRUE(view);
ASSERT_EQ(view.value_type(), entt::resolve<int>());
ASSERT_EQ(view[0].cast<const int &>(), 64);
}
TEST(SequenceContainer, FromConstAnyRef) {
std::vector<int> vec{64};
const entt::meta_any any = entt::forward_as_meta(vec);
auto view = any.as_sequence_container();
ASSERT_TRUE(view);
ASSERT_EQ(view.value_type(), entt::resolve<int>());
ASSERT_EQ(view[0].cast<const int &>(), 64);
}
TEST(SequenceContainer, FromConstAnyConstRef) {
std::vector<int> vec{64};
const entt::meta_any 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[0].cast<const int &>(), 64);
}
ENTT_DEBUG_TEST(SequenceContainerDeathTest, FromConstAny) {
const std::vector<int> vec{64};
const entt::meta_any any{vec};
auto view = any.as_sequence_container();
ASSERT_TRUE(view);
ASSERT_DEATH(view[0].cast<int &>() = 2, "");
}
ENTT_DEBUG_TEST(SequenceContainerDeathTest, FromConstAnyRef) {
std::vector<int> vec{64};
const entt::meta_any any = entt::forward_as_meta(vec);
auto view = any.as_sequence_container();
ASSERT_TRUE(view);
ASSERT_DEATH(view[0].cast<int &>() = 2, "");
}
ENTT_DEBUG_TEST(SequenceContainerDeathTest, FromConstAnyConstRef) {
std::vector<int> vec{64};
const entt::meta_any 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(AssociativeContainer, Empty) {
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(AssociativeContainer, Iterator) {
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(AssociativeContainer, 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_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(test::empty{}, 'a'));
ASSERT_FALSE(view.insert(1, test::empty{}));
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(test::empty{}), 0u);
ASSERT_FALSE(view.find(test::empty{}));
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_FALSE(view.reserve(8u));
ASSERT_EQ(view.size(), 0u);
}
TEST(AssociativeContainer, 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_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(test::empty{}));
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(test::empty{}), 0u);
ASSERT_FALSE(view.find(test::empty{}));
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_FALSE(view.reserve(8u));
ASSERT_EQ(view.size(), 0u);
}
TEST(AssociativeContainer, 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_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(test::empty{}, 'a'));
ASSERT_FALSE(view.insert(1, test::empty{}));
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(test::empty{}), 0u);
ASSERT_FALSE(view.find(test::empty{}));
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_TRUE(view.reserve(8u));
ASSERT_EQ(view.size(), 0u);
}
TEST(AssociativeContainer, 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_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(test::empty{}));
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(test::empty{}), 0u);
ASSERT_FALSE(view.find(test::empty{}));
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_TRUE(view.reserve(8u));
ASSERT_EQ(view.size(), 0u);
}
TEST(KeyValueAssociativeContainer, Constness) {
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_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_FALSE(view.reserve(8u));
ASSERT_EQ(view.size(), 1u);
}
ENTT_DEBUG_TEST(KeyValueAssociativeContainerDeathTest, Constness) {
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(KeyOnlyAssociativeContainer, Constness) {
std::set<int> set{};
auto any = entt::forward_as_meta(std::as_const(set));
auto view = any.as_associative_container();
ASSERT_TRUE(view);
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_FALSE(view.reserve(8u));
ASSERT_EQ(view.size(), 1u);
}
TEST(KeyValueAssociativeContainer, FromConstAny) {
const std::map<int, char> map{{2, 'c'}};
const entt::meta_any any{map};
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');
}
TEST(KeyValueAssociativeContainer, FromConstAnyRef) {
std::map<int, char> map{{2, 'c'}};
const entt::meta_any any = entt::forward_as_meta(map);
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');
}
TEST(KeyValueAssociativeContainer, FromConstAnyConstRef) {
std::map<int, char> map{{2, 'c'}};
const entt::meta_any any = entt::forward_as_meta(std::as_const(map));
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');
}
ENTT_DEBUG_TEST(KeyValueAssociativeContainerDeathTest, FromConstAny) {
const std::map<int, char> map{{2, 'c'}};
const entt::meta_any any{map};
auto view = any.as_associative_container();
ASSERT_TRUE(view);
ASSERT_DEATH(view.find(2)->second.cast<char &>() = 'a', "");
}
ENTT_DEBUG_TEST(KeyValueAssociativeContainerDeathTest, FromConstAnyRef) {
std::map<int, char> map{{2, 'c'}};
const entt::meta_any any = entt::forward_as_meta(map);
auto view = any.as_associative_container();
ASSERT_TRUE(view);
ASSERT_DEATH(view.find(2)->second.cast<char &>() = 'a', "");
}
ENTT_DEBUG_TEST(KeyValueAssociativeContainerDeathTest, FromConstAnyConstRef) {
std::map<int, char> map{{2, 'c'}};
const entt::meta_any 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(KeyOnlyAssociativeContainer, FromConstAny) {
const std::set<int> set{2};
const entt::meta_any any{set};
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);
}
TEST(KeyOnlyAssociativeContainer, FromConstAnyRef) {
std::set<int> set{2};
const entt::meta_any any = entt::forward_as_meta(set);
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);
}
TEST(KeyOnlyAssociativeContainer, FromConstAnyConstRef) {
std::set<int> set{2};
const entt::meta_any any = entt::forward_as_meta(std::as_const(set));
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);
}

View File

@@ -0,0 +1,509 @@
#include <iterator>
#include <unordered_map>
#include <utility>
#include <vector>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/core/type_info.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/empty.h"
struct base {
base() = default;
base(char cv)
: value{cv} {}
[[nodiscard]] char get() const {
return value;
}
char value;
};
struct clazz: base {
clazz()
: base{} {}
clazz(int iv)
: base{},
value{iv} {}
clazz(char cv, int iv) // NOLINT
: base{cv},
value{iv} {}
[[nodiscard]] int func(int iv) {
return (value = iv);
}
[[nodiscard]] int cfunc(int) const {
return value;
}
static void move_to_bucket(const clazz &instance) {
bucket = instance.value;
}
int value{};
inline static int bucket{}; // NOLINT
};
struct argument {
argument(int val)
: value{val} {}
[[nodiscard]] int get() const {
return value;
}
[[nodiscard]] int get_mul() const {
return value * 2;
}
private:
int value{};
};
template<typename...>
struct template_clazz {};
class MetaContext: public ::testing::Test {
static void init_global_context() {
using namespace entt::literals;
entt::meta_factory<int>{}
.data<global_marker>("marker"_hs);
entt::meta_factory<argument>{}
.conv<&argument::get>();
entt::meta_factory<clazz>{}
.type("foo"_hs)
.custom<int>(3)
.ctor<int>()
.data<&clazz::value>("value"_hs)
.data<&clazz::value>("rw"_hs)
.func<&clazz::func>("func"_hs);
entt::meta_factory<template_clazz<int>>{}
.type("template"_hs);
}
void init_local_context() {
using namespace entt::literals;
entt::meta_factory<int>{context}
.data<local_marker>("marker"_hs);
entt::meta_factory<test::empty>{context}
.type("quux"_hs);
entt::meta_factory<argument>{context}
.conv<&argument::get_mul>();
entt::meta_factory<base>{context}
.data<&base::value>("char"_hs)
.func<&base::get>("get"_hs);
entt::meta_factory<clazz>{context}
.type("bar"_hs)
.custom<char>('c')
.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_factory<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();
}
[[nodiscard]] const entt::meta_ctx &ctx() const noexcept {
return context;
}
protected:
static constexpr int global_marker = 1;
static constexpr int local_marker = 4;
static constexpr int bucket_value = 2;
private:
entt::meta_ctx context{};
};
TEST_F(MetaContext, Resolve) {
using namespace entt::literals;
ASSERT_TRUE(entt::resolve<clazz>());
ASSERT_TRUE(entt::resolve<clazz>(ctx()));
ASSERT_TRUE(entt::resolve<test::empty>());
ASSERT_TRUE(entt::resolve<test::empty>(ctx()));
ASSERT_TRUE(entt::resolve(entt::type_id<clazz>()));
ASSERT_TRUE(entt::resolve(ctx(), entt::type_id<clazz>()));
ASSERT_FALSE(entt::resolve(entt::type_id<test::empty>()));
ASSERT_TRUE(entt::resolve(ctx(), entt::type_id<test::empty>()));
ASSERT_TRUE(entt::resolve("foo"_hs));
ASSERT_FALSE(entt::resolve(ctx(), "foo"_hs));
ASSERT_FALSE(entt::resolve("bar"_hs));
ASSERT_TRUE(entt::resolve(ctx(), "bar"_hs));
ASSERT_FALSE(entt::resolve("quux"_hs));
ASSERT_TRUE(entt::resolve(ctx(), "quux"_hs));
ASSERT_EQ((std::distance(entt::resolve().cbegin(), entt::resolve().cend())), 4);
ASSERT_EQ((std::distance(entt::resolve(ctx()).cbegin(), entt::resolve(ctx()).cend())), 6);
}
TEST_F(MetaContext, MetaType) {
using namespace entt::literals;
const auto global = entt::resolve<clazz>();
const auto local = entt::resolve<clazz>(ctx());
ASSERT_TRUE(global);
ASSERT_TRUE(local);
ASSERT_NE(global, local);
ASSERT_EQ(global, entt::resolve("foo"_hs));
ASSERT_EQ(local, entt::resolve(ctx(), "bar"_hs));
ASSERT_EQ(global.id(), "foo"_hs);
ASSERT_EQ(local.id(), "bar"_hs);
clazz instance{'c', 8};
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>(), instance.value);
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>(ctx());
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(ctx(), entt::type_id<base>()));
}
TEST_F(MetaContext, MetaData) {
using namespace entt::literals;
const auto global = entt::resolve<clazz>();
const auto local = entt::resolve<clazz>(ctx());
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', 8};
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>(ctx());
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', 8};
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>(), instance.value);
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>(ctx());
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);
const 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(ctx(), 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>(ctx()).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, MetaCustom) {
using namespace entt::literals;
const auto global = entt::resolve<clazz>();
const auto local = entt::resolve<clazz>(ctx());
ASSERT_NE(static_cast<const int *>(global.custom()), nullptr);
ASSERT_NE(static_cast<const char *>(local.custom()), nullptr);
ASSERT_EQ(static_cast<int>(global.custom()), 3);
ASSERT_EQ(static_cast<char>(local.custom()), 'c');
}
TEST_F(MetaContext, MetaTemplate) {
using namespace entt::literals;
const auto global = entt::resolve("template"_hs);
const auto local = entt::resolve(ctx(), "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>(ctx()));
ASSERT_EQ(local.template_arg(1u), entt::resolve<char>(ctx()));
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 = 2;
const entt::meta_any global{&value};
const entt::meta_any local{ctx(), &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(ctx(), 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(ctx(), 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;
const entt::meta_any global{2};
const entt::meta_any ctx_value{ctx(), 2};
const entt::meta_any in_place{ctx(), std::in_place_type<int>, 2};
entt::meta_any two_step_local{entt::meta_ctx_arg, ctx()};
ASSERT_TRUE(global);
ASSERT_TRUE(ctx_value);
ASSERT_TRUE(in_place);
ASSERT_FALSE(two_step_local);
two_step_local = 2;
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 = 2;
entt::meta_handle global{value};
entt::meta_handle ctx_value{ctx(), value};
entt::meta_handle two_step_local{entt::meta_ctx_arg, ctx()};
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(2);
const auto local = entt::forward_as_meta(ctx(), 2);
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,72 @@
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/locator/locator.hpp>
#include <entt/meta/context.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/node.hpp>
#include <entt/meta/resolve.hpp>
struct clazz {
clazz() = default;
operator int() const {
return value;
}
[[nodiscard]] bool to_bool() const {
return (value != 0);
}
int value{};
};
double conv_to_double(const clazz &instance) {
return instance.value * 2.;
}
struct MetaConv: ::testing::Test {
void SetUp() override {
using namespace entt::literals;
entt::meta_factory<clazz>{}
.type("clazz"_hs)
.conv<int>()
.conv<&clazz::to_bool>()
.conv<conv_to_double>();
}
void TearDown() override {
entt::meta_reset();
}
};
TEST_F(MetaConv, Conv) {
auto any = entt::resolve<clazz>().construct();
any.cast<clazz &>().value = 2;
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 &>().operator int());
ASSERT_EQ(as_bool.cast<bool>(), any.cast<clazz &>().to_bool());
ASSERT_EQ(as_double.cast<double>(), conv_to_double(any.cast<clazz &>()));
}
TEST_F(MetaConv, ReRegistration) {
SetUp();
auto &&node = entt::internal::resolve<clazz>(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,214 @@
#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/context.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/policy.hpp>
#include <entt/meta/resolve.hpp>
struct base {
char value{'c'};
};
struct derived: base {
derived()
: base{} {}
};
struct clazz {
clazz(const base &other, int &iv)
: clazz{iv, other.value} {}
clazz(const int &iv, char cv)
: i{iv}, c{cv} {}
operator int() const {
return i;
}
static clazz factory(int value) {
return {value, 'c'};
}
static clazz factory(base other, int value, int mul) {
return {value * mul, other.value};
}
int i{};
char c{};
};
double double_factory() {
return 1.;
}
struct MetaCtor: ::testing::Test {
void SetUp() override {
using namespace entt::literals;
entt::meta_factory<double>{}
.type("double"_hs)
.ctor<double_factory>();
entt::meta_factory<derived>{}
.type("derived"_hs)
.base<base>();
entt::meta_factory<clazz>{}
.type("clazz"_hs)
.ctor<&entt::registry::emplace_or_replace<clazz, const int &, const char &>, entt::as_ref_t>()
.ctor<const base &, int &>()
.ctor<const int &, char>()
.ctor<entt::overload<clazz(int)>(clazz::factory)>()
.ctor<entt::overload<clazz(base, int, int)>(clazz::factory)>()
.conv<int>();
}
void TearDown() override {
entt::meta_reset();
}
};
TEST_F(MetaCtor, Ctor) {
auto any = entt::resolve<clazz>().construct(1, 'c');
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz>().i, 1);
ASSERT_EQ(any.cast<clazz>().c, 'c');
}
TEST_F(MetaCtor, Func) {
auto any = entt::resolve<clazz>().construct(1);
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz>().i, 1);
ASSERT_EQ(any.cast<clazz>().c, 'c');
}
TEST_F(MetaCtor, MetaAnyArgs) {
auto any = entt::resolve<clazz>().construct(entt::meta_any{1}, entt::meta_any{'c'});
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz>().i, 1);
ASSERT_EQ(any.cast<clazz>().c, 'c');
}
TEST_F(MetaCtor, InvalidArgs) {
ASSERT_FALSE(entt::resolve<clazz>().construct(entt::meta_any{}, derived{}));
}
TEST_F(MetaCtor, CastAndConvert) {
auto any = entt::resolve<clazz>().construct(derived{}, clazz{1, 'd'});
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz>().i, 1);
ASSERT_EQ(any.cast<clazz>().c, 'c');
}
TEST_F(MetaCtor, ArithmeticConversion) {
auto any = entt::resolve<clazz>().construct(true, 4.2);
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz>().i, 1);
ASSERT_EQ(any.cast<clazz>().c, char{4});
}
TEST_F(MetaCtor, ConstNonConstRefArgs) {
int ivalue = 1;
const char cvalue = 'c';
auto any = entt::resolve<clazz>().construct(entt::forward_as_meta(ivalue), entt::forward_as_meta(cvalue));
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz>().i, 1);
ASSERT_EQ(any.cast<clazz>().c, 'c');
}
TEST_F(MetaCtor, WrongConstness) {
int value = 1;
auto any = entt::resolve<clazz>().construct(derived{}, entt::forward_as_meta(value));
auto other = entt::resolve<clazz>().construct(derived{}, entt::forward_as_meta(std::as_const(value)));
ASSERT_TRUE(any);
ASSERT_FALSE(other);
ASSERT_EQ(any.cast<clazz>().i, 1);
ASSERT_EQ(any.cast<clazz>().c, 'c');
}
TEST_F(MetaCtor, FuncMetaAnyArgs) {
auto any = entt::resolve<clazz>().construct(entt::meta_any{1});
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz>().i, 1);
ASSERT_EQ(any.cast<clazz>().c, 'c');
}
TEST_F(MetaCtor, FuncCastAndConvert) {
auto any = entt::resolve<clazz>().construct(derived{}, 3., clazz{3, 'd'});
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz>().i, 9);
ASSERT_EQ(any.cast<clazz>().c, 'c');
}
TEST_F(MetaCtor, FuncArithmeticConversion) {
auto any = entt::resolve<clazz>().construct(4.2);
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz>().i, 4);
ASSERT_EQ(any.cast<clazz>().c, 'c');
}
TEST_F(MetaCtor, FuncConstNonConstRefArgs) {
int ivalue = 1;
auto any = entt::resolve<clazz>().construct(entt::forward_as_meta(ivalue));
auto other = entt::resolve<clazz>().construct(entt::forward_as_meta(std::as_const(ivalue)));
ASSERT_TRUE(any);
ASSERT_TRUE(other);
ASSERT_EQ(any.cast<clazz>().i, 1);
ASSERT_EQ(other.cast<clazz>().i, 1);
}
TEST_F(MetaCtor, ExternalMemberFunction) {
entt::registry registry;
const auto entity = registry.create();
ASSERT_FALSE(registry.all_of<clazz>(entity));
const auto any = entt::resolve<clazz>().construct(entt::forward_as_meta(registry), entity, 3, 'c');
ASSERT_TRUE(any);
ASSERT_TRUE(registry.all_of<clazz>(entity));
ASSERT_EQ(registry.get<clazz>(entity).i, 3);
ASSERT_EQ(registry.get<clazz>(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>(), 1.);
}
TEST_F(MetaCtor, NonDefaultConstructibleType) {
auto type = entt::resolve<clazz>();
// 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,141 @@
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/resolve.hpp>
#include "../../common/config.h"
struct clazz {
int i{2};
char j{'c'};
[[nodiscard]] int f(int) const {
return i;
}
[[nodiscard]] char g(char) const {
return j;
}
};
struct MetaCustom: ::testing::Test {
void SetUp() override {
using namespace entt::literals;
entt::meta_factory<clazz>{}
.type("clazz"_hs)
.custom<char>('c')
.data<&clazz::i>("i"_hs)
.custom<int>(0)
.data<&clazz::j>("j"_hs)
.func<&clazz::f>("f"_hs)
.custom<int>(1)
.func<&clazz::g>("g"_hs);
}
void TearDown() override {
entt::meta_reset();
}
};
using MetaCustomDeathTest = MetaCustom;
TEST_F(MetaCustom, Custom) {
entt::meta_custom custom{};
ASSERT_EQ(static_cast<const char *>(custom), nullptr);
custom = entt::resolve<clazz>().custom();
ASSERT_NE(static_cast<const char *>(custom), nullptr);
ASSERT_EQ(*static_cast<const char *>(custom), 'c');
ASSERT_EQ(static_cast<const char &>(custom), 'c');
}
ENTT_DEBUG_TEST_F(MetaCustomDeathTest, Custom) {
entt::meta_custom custom{};
ASSERT_DEATH([[maybe_unused]] const char &value = custom, "");
custom = entt::resolve<clazz>().custom();
ASSERT_DEATH([[maybe_unused]] const int &value = custom, "");
}
TEST_F(MetaCustom, Type) {
ASSERT_NE(static_cast<const char *>(entt::resolve<clazz>().custom()), nullptr);
ASSERT_EQ(*static_cast<const char *>(entt::resolve<clazz>().custom()), 'c');
ASSERT_EQ(static_cast<const char &>(entt::resolve<clazz>().custom()), 'c');
ASSERT_EQ(static_cast<const int *>(entt::resolve<clazz>().custom()), nullptr);
ASSERT_EQ(static_cast<const char *>(entt::resolve<int>().custom()), nullptr);
}
TEST_F(MetaCustom, Data) {
using namespace entt::literals;
const clazz instance{};
ASSERT_TRUE(entt::resolve<clazz>().data("i"_hs));
ASSERT_EQ(entt::resolve<clazz>().get("i"_hs, instance).cast<int>(), 2);
ASSERT_TRUE(entt::resolve<clazz>().data("j"_hs));
ASSERT_EQ(entt::resolve<clazz>().get("j"_hs, instance).cast<char>(), 'c');
ASSERT_NE(static_cast<const int *>(entt::resolve<clazz>().data("i"_hs).custom()), nullptr);
ASSERT_EQ(*static_cast<const int *>(entt::resolve<clazz>().data("i"_hs).custom()), 0);
ASSERT_EQ(static_cast<const int &>(entt::resolve<clazz>().data("i"_hs).custom()), 0);
ASSERT_EQ(static_cast<const char *>(entt::resolve<clazz>().data("i"_hs).custom()), nullptr);
ASSERT_EQ(static_cast<const int *>(entt::resolve<clazz>().data("j"_hs).custom()), nullptr);
}
TEST_F(MetaCustom, Func) {
using namespace entt::literals;
const clazz instance{};
ASSERT_TRUE(entt::resolve<clazz>().func("f"_hs));
ASSERT_EQ(entt::resolve<clazz>().invoke("f"_hs, instance, 0).cast<int>(), 2);
ASSERT_TRUE(entt::resolve<clazz>().func("g"_hs));
ASSERT_EQ(entt::resolve<clazz>().invoke("g"_hs, instance, 'c').cast<char>(), 'c');
ASSERT_NE(static_cast<const int *>(entt::resolve<clazz>().func("f"_hs).custom()), nullptr);
ASSERT_EQ(*static_cast<const int *>(entt::resolve<clazz>().func("f"_hs).custom()), 1);
ASSERT_EQ(static_cast<const int &>(entt::resolve<clazz>().func("f"_hs).custom()), 1);
ASSERT_EQ(static_cast<const char *>(entt::resolve<clazz>().func("f"_hs).custom()), nullptr);
ASSERT_EQ(static_cast<const int *>(entt::resolve<clazz>().func("g"_hs).custom()), nullptr);
}
TEST_F(MetaCustom, ConstNonConstAndAllInBetween) {
testing::StaticAssertTypeEq<decltype(static_cast<int *>(entt::meta_custom{})), int *>();
testing::StaticAssertTypeEq<decltype(static_cast<int &>(entt::meta_custom{})), int &>();
testing::StaticAssertTypeEq<decltype(static_cast<const int *>(entt::meta_custom{})), const int *>();
testing::StaticAssertTypeEq<decltype(static_cast<const int &>(entt::meta_custom{})), const int &>();
static_cast<char &>(entt::resolve<clazz>().custom()) = '\n';
ASSERT_EQ(*static_cast<const char *>(entt::resolve<clazz>().custom()), '\n');
}
TEST_F(MetaCustom, ReRegistration) {
using namespace entt::literals;
SetUp();
auto type = entt::resolve<clazz>();
ASSERT_EQ(static_cast<const int *>(type.custom()), nullptr);
ASSERT_NE(static_cast<const char *>(type.custom()), nullptr);
ASSERT_EQ(*static_cast<const char *>(type.custom()), 'c');
entt::meta_factory<clazz>{}.custom<int>(1);
type = entt::resolve<clazz>();
ASSERT_NE(static_cast<const int *>(type.custom()), nullptr);
ASSERT_EQ(static_cast<const char *>(type.custom()), nullptr);
ASSERT_EQ(*static_cast<const int *>(type.custom()), 1);
}

View File

@@ -0,0 +1,707 @@
#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/context.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/node.hpp>
#include <entt/meta/policy.hpp>
#include <entt/meta/range.hpp>
#include <entt/meta/resolve.hpp>
#include "../../common/config.h"
#include "../../common/meta_traits.h"
struct base {
virtual ~base() = default;
static void destroy(base &) {
++counter;
}
inline static int counter = 0; // NOLINT
int value{3};
};
struct derived: base {
derived() = default;
};
struct clazz {
operator int() const {
return h;
}
int i{0};
const int j{1}; // NOLINT
base instance{};
inline static int h{2}; // NOLINT
inline static const int k{3}; // NOLINT
};
struct setter_getter {
int setter(double val) {
return value = static_cast<int>(val);
}
[[nodiscard]] int getter() const {
return value;
}
int setter_with_ref(const int &val) {
return value = val;
}
[[nodiscard]] const int &getter_with_ref() const {
return value;
}
static int static_setter(setter_getter &type, int value) {
return type.value = value;
}
static int static_getter(const setter_getter &type) {
return type.value;
}
int value{0};
};
struct multi_setter {
void from_double(double val) {
value = static_cast<int>(val);
}
void from_string(const char *val) {
value = std::atoi(val);
}
int value{0};
};
struct array {
inline static int global[2]; // NOLINT
int local[4]; // NOLINT
};
struct MetaData: ::testing::Test {
void SetUp() override {
using namespace entt::literals;
entt::meta_factory<base>{}
.type("base"_hs)
.dtor<base::destroy>()
.data<&base::value>("value"_hs);
entt::meta_factory<derived>{}
.type("derived"_hs)
.base<base>()
.dtor<derived::destroy>()
.data<&base::value>("value_from_base"_hs);
entt::meta_factory<clazz>{}
.type("clazz"_hs)
.data<&clazz::i, entt::as_ref_t>("i"_hs)
.custom<char>('c')
.traits(test::meta_traits::one | test::meta_traits::two | test::meta_traits::three)
.data<&clazz::i, entt::as_cref_t>("ci"_hs)
.data<&clazz::j>("j"_hs)
.traits(test::meta_traits::one)
.data<&clazz::h>("h"_hs)
.traits(test::meta_traits::two)
.data<&clazz::k>("k"_hs)
.traits(test::meta_traits::three)
.data<'c'>("l"_hs)
.data<&clazz::instance>("base"_hs)
.data<&clazz::i, entt::as_void_t>("void"_hs)
.conv<int>();
entt::meta_factory<setter_getter>{}
.type("setter_getter"_hs)
.data<&setter_getter::static_setter, &setter_getter::static_getter>("x"_hs)
.data<&setter_getter::setter, &setter_getter::getter>("y"_hs)
.data<&setter_getter::static_setter, &setter_getter::getter>("z"_hs)
.data<&setter_getter::setter_with_ref, &setter_getter::getter_with_ref>("w"_hs)
.data<nullptr, &setter_getter::getter>("z_ro"_hs)
.data<nullptr, &setter_getter::value>("value"_hs);
entt::meta_factory<multi_setter>{}
.type("multi_setter"_hs)
.data<entt::value_list<&multi_setter::from_double, &multi_setter::from_string>, &multi_setter::value>("value"_hs);
entt::meta_factory<array>{}
.type("array"_hs)
.data<&array::global>("global"_hs)
.data<&array::local>("local"_hs);
base::counter = 0;
}
void TearDown() override {
entt::meta_reset();
}
};
using MetaDataDeathTest = MetaData;
TEST_F(MetaData, SafeWhenEmpty) {
const entt::meta_data data{};
ASSERT_FALSE(data);
ASSERT_EQ(data, entt::meta_data{});
ASSERT_EQ(data.arity(), 0u);
ASSERT_FALSE(data.is_const());
ASSERT_FALSE(data.is_static());
ASSERT_EQ(data.type(), entt::meta_type{});
ASSERT_FALSE(data.set({}, 0));
ASSERT_FALSE(data.get({}));
ASSERT_EQ(data.arg(0u), entt::meta_type{});
ASSERT_EQ(data.traits<test::meta_traits>(), test::meta_traits::none);
ASSERT_EQ(static_cast<const char *>(data.custom()), nullptr);
}
TEST_F(MetaData, UserTraits) {
using namespace entt::literals;
ASSERT_EQ(entt::resolve<clazz>().data("ci"_hs).traits<test::meta_traits>(), test::meta_traits::none);
ASSERT_EQ(entt::resolve<clazz>().data("base"_hs).traits<test::meta_traits>(), test::meta_traits::none);
ASSERT_EQ(entt::resolve<clazz>().data("i"_hs).traits<test::meta_traits>(), test::meta_traits::one | test::meta_traits::two | test::meta_traits::three);
ASSERT_EQ(entt::resolve<clazz>().data("j"_hs).traits<test::meta_traits>(), test::meta_traits::one);
ASSERT_EQ(entt::resolve<clazz>().data("h"_hs).traits<test::meta_traits>(), test::meta_traits::two);
ASSERT_EQ(entt::resolve<clazz>().data("k"_hs).traits<test::meta_traits>(), test::meta_traits::three);
}
ENTT_DEBUG_TEST_F(MetaDataDeathTest, UserTraits) {
using namespace entt::literals;
using traits_type = entt::internal::meta_traits;
constexpr auto value = traits_type{static_cast<std::underlying_type_t<traits_type>>(traits_type::_user_defined_traits) + 1u};
ASSERT_DEATH(entt::meta_factory<clazz>{}.data<&clazz::i>("j"_hs).traits(value), "");
}
TEST_F(MetaData, Custom) {
using namespace entt::literals;
ASSERT_EQ(*static_cast<const char *>(entt::resolve<clazz>().data("i"_hs).custom()), 'c');
ASSERT_EQ(static_cast<const char &>(entt::resolve<clazz>().data("i"_hs).custom()), 'c');
ASSERT_EQ(static_cast<const int *>(entt::resolve<clazz>().data("i"_hs).custom()), nullptr);
ASSERT_EQ(static_cast<const int *>(entt::resolve<clazz>().data("j"_hs).custom()), nullptr);
}
ENTT_DEBUG_TEST_F(MetaDataDeathTest, Custom) {
using namespace entt::literals;
ASSERT_DEATH([[maybe_unused]] const int value = entt::resolve<clazz>().data("i"_hs).custom(), "");
ASSERT_DEATH([[maybe_unused]] const char value = entt::resolve<clazz>().data("j"_hs).custom(), "");
}
TEST_F(MetaData, Comparison) {
using namespace entt::literals;
auto data = entt::resolve<clazz>().data("i"_hs);
ASSERT_TRUE(data);
ASSERT_EQ(data, data);
ASSERT_NE(data, entt::meta_data{});
ASSERT_FALSE(data != data);
ASSERT_TRUE(data == data);
}
TEST_F(MetaData, NonConst) {
using namespace entt::literals;
auto data = entt::resolve<clazz>().data("i"_hs);
clazz 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, 1));
ASSERT_EQ(data.get(instance).cast<int>(), 1);
}
TEST_F(MetaData, Const) {
using namespace entt::literals;
auto data = entt::resolve<clazz>().data("j"_hs);
clazz 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, 1));
ASSERT_EQ(data.get(instance).cast<int>(), 1);
}
TEST_F(MetaData, Static) {
using namespace entt::literals;
auto data = entt::resolve<clazz>().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({}, 1));
ASSERT_EQ(data.get({}).cast<int>(), 1);
}
TEST_F(MetaData, ConstStatic) {
using namespace entt::literals;
auto data = entt::resolve<clazz>().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({}, 1));
ASSERT_EQ(data.get({}).cast<int>(), 3);
}
TEST_F(MetaData, Literal) {
using namespace entt::literals;
auto data = entt::resolve<clazz>().data("l"_hs);
ASSERT_TRUE(data);
ASSERT_EQ(data.arity(), 1u);
ASSERT_EQ(data.type(), entt::resolve<char>());
ASSERT_EQ(data.arg(0u), entt::resolve<char>());
ASSERT_TRUE(data.is_const());
ASSERT_TRUE(data.is_static());
ASSERT_EQ(data.get({}).cast<char>(), 'c');
ASSERT_FALSE(data.set({}, 'a'));
ASSERT_EQ(data.get({}).cast<char>(), 'c');
}
TEST_F(MetaData, GetMetaAnyArg) {
using namespace entt::literals;
entt::meta_any any{clazz{}};
any.cast<clazz &>().i = 3;
const auto value = entt::resolve<clazz>().data("i"_hs).get(any);
ASSERT_TRUE(value);
ASSERT_TRUE(static_cast<bool>(value.cast<int>()));
ASSERT_EQ(value.cast<int>(), 3);
}
TEST_F(MetaData, GetInvalidArg) {
using namespace entt::literals;
auto instance = 0;
ASSERT_FALSE(entt::resolve<clazz>().data("i"_hs).get(instance));
}
TEST_F(MetaData, SetMetaAnyArg) {
using namespace entt::literals;
entt::meta_any any{clazz{}};
const entt::meta_any value{1};
ASSERT_EQ(any.cast<clazz>().i, 0);
ASSERT_TRUE(entt::resolve<clazz>().data("i"_hs).set(any, value));
ASSERT_EQ(any.cast<clazz>().i, 1);
}
TEST_F(MetaData, SetInvalidArg) {
using namespace entt::literals;
ASSERT_FALSE(entt::resolve<clazz>().data("i"_hs).set({}, 'c'));
}
TEST_F(MetaData, SetCast) {
using namespace entt::literals;
clazz instance{};
ASSERT_EQ(base::counter, 0);
ASSERT_TRUE(entt::resolve<clazz>().data("base"_hs).set(instance, derived{}));
ASSERT_EQ(base::counter, 1);
}
TEST_F(MetaData, SetConvert) {
using namespace entt::literals;
clazz instance{};
clazz::h = 1;
ASSERT_EQ(instance.i, 0);
ASSERT_TRUE(entt::resolve<clazz>().data("i"_hs).set(instance, instance));
ASSERT_EQ(instance.i, 1);
}
TEST_F(MetaData, SetByRef) {
using namespace entt::literals;
entt::meta_any any{clazz{}};
int value{1};
ASSERT_EQ(any.cast<clazz>().i, 0);
ASSERT_TRUE(entt::resolve<clazz>().data("i"_hs).set(any, entt::forward_as_meta(value)));
ASSERT_EQ(any.cast<clazz>().i, 1);
value = 3;
auto wrapper = entt::forward_as_meta(value);
ASSERT_TRUE(entt::resolve<clazz>().data("i"_hs).set(any, wrapper.as_ref()));
ASSERT_EQ(any.cast<clazz>().i, 3);
}
TEST_F(MetaData, SetByConstRef) {
using namespace entt::literals;
entt::meta_any any{clazz{}};
int value{1};
ASSERT_EQ(any.cast<clazz>().i, 0);
ASSERT_TRUE(entt::resolve<clazz>().data("i"_hs).set(any, entt::forward_as_meta(std::as_const(value))));
ASSERT_EQ(any.cast<clazz>().i, 1);
value = 3;
auto wrapper = entt::forward_as_meta(std::as_const(value));
ASSERT_TRUE(entt::resolve<clazz>().data("i"_hs).set(any, wrapper.as_ref()));
ASSERT_EQ(any.cast<clazz>().i, 3);
}
TEST_F(MetaData, SetterGetterAsFreeFunctions) {
using namespace entt::literals;
auto data = entt::resolve<setter_getter>().data("x"_hs);
setter_getter 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, 1));
ASSERT_EQ(data.get(instance).cast<int>(), 1);
}
TEST_F(MetaData, SetterGetterAsMemberFunctions) {
using namespace entt::literals;
auto data = entt::resolve<setter_getter>().data("y"_hs);
setter_getter 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, 1.));
ASSERT_EQ(data.get(instance).cast<int>(), 1);
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>().data("w"_hs);
setter_getter 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, 1));
ASSERT_EQ(data.get(instance).cast<int>(), 1);
}
TEST_F(MetaData, SetterGetterMixed) {
using namespace entt::literals;
auto data = entt::resolve<setter_getter>().data("z"_hs);
setter_getter 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, 1));
ASSERT_EQ(data.get(instance).cast<int>(), 1);
}
TEST_F(MetaData, SetterGetterReadOnly) {
using namespace entt::literals;
auto data = entt::resolve<setter_getter>().data("z_ro"_hs);
setter_getter 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, 1));
ASSERT_EQ(data.get(instance).cast<int>(), 0);
}
TEST_F(MetaData, SetterGetterReadOnlyDataMember) {
using namespace entt::literals;
auto data = entt::resolve<setter_getter>().data("value"_hs);
setter_getter 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, 1));
ASSERT_EQ(data.get(instance).cast<int>(), 0);
}
TEST_F(MetaData, MultiSetter) {
using namespace entt::literals;
auto data = entt::resolve<multi_setter>().data("value"_hs);
multi_setter 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, 1));
ASSERT_EQ(data.get(instance).cast<int>(), 1);
ASSERT_TRUE(data.set(instance, 2.));
ASSERT_EQ(data.get(instance).cast<int>(), 2);
ASSERT_FALSE(data.set(instance, std::string{"3"}));
ASSERT_TRUE(data.set(instance, std::string{"3"}.c_str()));
ASSERT_EQ(data.get(instance).cast<int>(), 3);
}
TEST_F(MetaData, ConstInstance) {
using namespace entt::literals;
clazz instance{};
ASSERT_NE(entt::resolve<clazz>().data("i"_hs).get(instance).try_cast<int>(), nullptr);
ASSERT_NE(entt::resolve<clazz>().data("i"_hs).get(instance).try_cast<const int>(), nullptr);
ASSERT_EQ(entt::resolve<clazz>().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>().data("i"_hs).get(std::as_const(instance)).try_cast<const int>(), nullptr);
ASSERT_TRUE(entt::resolve<clazz>().data("i"_hs).get(instance));
ASSERT_TRUE(entt::resolve<clazz>().data("i"_hs).set(instance, 3));
ASSERT_TRUE(entt::resolve<clazz>().data("i"_hs).get(std::as_const(instance)));
ASSERT_FALSE(entt::resolve<clazz>().data("i"_hs).set(std::as_const(instance), 3));
ASSERT_TRUE(entt::resolve<clazz>().data("ci"_hs).get(instance));
ASSERT_TRUE(entt::resolve<clazz>().data("ci"_hs).set(instance, 3));
ASSERT_TRUE(entt::resolve<clazz>().data("ci"_hs).get(std::as_const(instance)));
ASSERT_FALSE(entt::resolve<clazz>().data("ci"_hs).set(std::as_const(instance), 3));
ASSERT_TRUE(entt::resolve<clazz>().data("j"_hs).get(instance));
ASSERT_FALSE(entt::resolve<clazz>().data("j"_hs).set(instance, 3));
ASSERT_TRUE(entt::resolve<clazz>().data("j"_hs).get(std::as_const(instance)));
ASSERT_FALSE(entt::resolve<clazz>().data("j"_hs).set(std::as_const(instance), 3));
}
TEST_F(MetaData, ArrayStatic) {
using namespace entt::literals;
auto data = entt::resolve<array>().data("global"_hs);
ASSERT_TRUE(data);
ASSERT_EQ(data.arity(), 1u);
// NOLINTBEGIN(*-avoid-c-arrays)
ASSERT_EQ(data.type(), entt::resolve<int[2]>());
ASSERT_EQ(data.arg(0u), entt::resolve<int[2]>());
// NOLINTEND(*-avoid-c-arrays)
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>().data("local"_hs);
array instance{};
ASSERT_TRUE(data);
ASSERT_EQ(data.arity(), 1u);
// NOLINTBEGIN(*-avoid-c-arrays)
ASSERT_EQ(data.type(), entt::resolve<int[4]>());
ASSERT_EQ(data.arg(0u), entt::resolve<int[4]>());
// NOLINTEND(*-avoid-c-arrays)
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>().data("void"_hs);
clazz 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, 1));
ASSERT_EQ(instance.i, 1);
ASSERT_EQ(data.get(instance), entt::meta_any{std::in_place_type<void>});
}
TEST_F(MetaData, AsRef) {
using namespace entt::literals;
clazz instance{};
auto data = entt::resolve<clazz>().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_EQ(instance.i, 0);
data.get(instance).cast<int &>() = 3;
ASSERT_EQ(instance.i, 3);
}
TEST_F(MetaData, AsConstRef) {
using namespace entt::literals;
clazz instance{};
auto data = entt::resolve<clazz>().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(instance.i, 0);
}
ENTT_DEBUG_TEST_F(MetaDataDeathTest, AsConstRef) {
using namespace entt::literals;
clazz instance{};
auto data = entt::resolve<clazz>().data("ci"_hs);
ASSERT_DEATH(data.get(instance).cast<int &>() = 3, "");
}
TEST_F(MetaData, SetGetBaseData) {
using namespace entt::literals;
auto type = entt::resolve<derived>();
derived instance{};
ASSERT_TRUE(type.data("value"_hs));
ASSERT_EQ(instance.value, 3);
ASSERT_TRUE(type.data("value"_hs).set(instance, 1));
ASSERT_EQ(type.data("value"_hs).get(instance).cast<int>(), 1);
ASSERT_EQ(instance.value, 1);
}
TEST_F(MetaData, SetGetFromBase) {
using namespace entt::literals;
auto type = entt::resolve<derived>();
derived instance{};
ASSERT_TRUE(type.data("value_from_base"_hs));
ASSERT_EQ(instance.value, 3);
ASSERT_TRUE(type.data("value_from_base"_hs).set(instance, 1));
ASSERT_EQ(type.data("value_from_base"_hs).get(instance).cast<int>(), 1);
ASSERT_EQ(instance.value, 1);
}
TEST_F(MetaData, ReRegistration) {
using namespace entt::literals;
SetUp();
auto &&node = entt::internal::resolve<base>(entt::internal::meta_context::from(entt::locator<entt::meta_ctx>::value_or()));
auto type = entt::resolve<base>();
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_factory<base>{}.data<&base::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));
entt::meta_factory<base>{}
.data<&base::value>("field"_hs)
.traits(test::meta_traits::one)
.custom<int>(3)
// this should not overwrite traits and custom data
.data<&base::value>("field"_hs);
ASSERT_EQ(type.data("field"_hs).traits<test::meta_traits>(), test::meta_traits::one);
ASSERT_NE(static_cast<const int *>(type.data("field"_hs).custom()), nullptr);
}
TEST_F(MetaData, CollisionAndReuse) {
using namespace entt::literals;
ASSERT_TRUE(entt::resolve<clazz>().data("j"_hs));
ASSERT_FALSE(entt::resolve<clazz>().data("cj"_hs));
ASSERT_TRUE(entt::resolve<clazz>().data("j"_hs).is_const());
ASSERT_NO_THROW(entt::meta_factory<clazz>{}.data<&clazz::i>("j"_hs));
ASSERT_NO_THROW(entt::meta_factory<clazz>{}.data<&clazz::j>("cj"_hs));
ASSERT_TRUE(entt::resolve<clazz>().data("j"_hs));
ASSERT_TRUE(entt::resolve<clazz>().data("cj"_hs));
ASSERT_FALSE(entt::resolve<clazz>().data("j"_hs).is_const());
}

View File

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

View File

@@ -0,0 +1,509 @@
#include <array>
#include <iterator>
#include <string>
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/type_info.hpp>
#include <entt/meta/context.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/range.hpp>
#include <entt/meta/resolve.hpp>
#include "../../common/boxed_type.h"
#include "../../common/config.h"
#include "../../common/meta_traits.h"
struct base {
char member{};
};
struct clazz: base {
clazz(int val)
: value{val} {}
[[nodiscard]] explicit operator int() const noexcept {
return get_int();
}
void set_int(int val) noexcept {
value = val;
}
void set_boxed_int(test::boxed_int val) noexcept {
value = val.value;
}
[[nodiscard]] int get_int() const noexcept {
return value;
}
[[nodiscard]] static std::string to_string(const clazz &instance) {
return std::to_string(instance.get_int());
}
[[nodiscard]] static clazz from_string(const std::string &value) {
return clazz{std::stoi(value)};
}
private:
int value{};
};
struct dtor_callback {
dtor_callback(bool &ref)
: cb{&ref} {}
static void on_destroy(dtor_callback &instance) {
*instance.cb = !*instance.cb;
}
private:
bool *cb;
};
struct MetaFactory: ::testing::Test {
void TearDown() override {
entt::meta_reset();
}
};
using MetaFactoryDeathTest = MetaFactory;
TEST_F(MetaFactory, Constructors) {
entt::meta_ctx ctx{};
ASSERT_EQ(entt::resolve(entt::type_id<int>()), entt::meta_type{});
ASSERT_EQ(entt::resolve(ctx, entt::type_id<int>()), entt::meta_type{});
entt::meta_factory<int> factory{};
ASSERT_NE(entt::resolve(entt::type_id<int>()), entt::meta_type{});
ASSERT_EQ(entt::resolve(ctx, entt::type_id<int>()), entt::meta_type{});
ASSERT_TRUE(entt::resolve(entt::type_id<int>()).is_integral());
factory = entt::meta_factory<int>{ctx};
ASSERT_NE(entt::resolve(entt::type_id<int>()), entt::meta_type{});
ASSERT_NE(entt::resolve(ctx, entt::type_id<int>()), entt::meta_type{});
ASSERT_TRUE(entt::resolve(ctx, entt::type_id<int>()).is_integral());
}
TEST_F(MetaFactory, Type) {
using namespace entt::literals;
entt::meta_factory<int> factory{};
ASSERT_EQ(entt::resolve("foo"_hs), entt::meta_type{});
factory.type("foo"_hs);
ASSERT_NE(entt::resolve("foo"_hs), entt::meta_type{});
ASSERT_EQ(entt::resolve<int>().id(), "foo"_hs);
factory.type("bar"_hs);
ASSERT_EQ(entt::resolve("foo"_hs), entt::meta_type{});
ASSERT_NE(entt::resolve("bar"_hs), entt::meta_type{});
ASSERT_EQ(entt::resolve<int>().id(), "bar"_hs);
}
ENTT_DEBUG_TEST_F(MetaFactoryDeathTest, Type) {
using namespace entt::literals;
entt::meta_factory<int> factory{};
entt::meta_factory<double> other{};
factory.type("foo"_hs);
ASSERT_DEATH(other.type("foo"_hs);, "");
}
TEST_F(MetaFactory, Base) {
entt::meta_factory<clazz> factory{};
decltype(std::declval<entt::meta_type>().base()) range{};
ASSERT_NE(entt::resolve(entt::type_id<clazz>()), entt::meta_type{});
ASSERT_EQ(entt::resolve(entt::type_id<base>()), entt::meta_type{});
range = entt::resolve<clazz>().base();
ASSERT_EQ(range.begin(), range.end());
factory.base<base>();
range = entt::resolve<clazz>().base();
ASSERT_EQ(entt::resolve(entt::type_id<base>()), entt::meta_type{});
ASSERT_NE(range.begin(), range.end());
ASSERT_EQ(std::distance(range.begin(), range.end()), 1);
ASSERT_EQ(range.begin()->first, entt::type_id<base>().hash());
ASSERT_EQ(range.begin()->second.info(), entt::type_id<base>());
}
TEST_F(MetaFactory, Conv) {
const clazz instance{3};
entt::meta_factory<clazz> factory{};
const entt::meta_any any = entt::forward_as_meta(instance);
ASSERT_FALSE(any.allow_cast<int>());
ASSERT_FALSE(any.allow_cast<std::string>());
factory.conv<int>().conv<&clazz::to_string>();
ASSERT_TRUE(any.allow_cast<int>());
ASSERT_TRUE(any.allow_cast<std::string>());
ASSERT_EQ(any.allow_cast<int>().cast<int>(), instance.get_int());
ASSERT_EQ(any.allow_cast<std::string>().cast<std::string>(), clazz::to_string(instance));
}
TEST_F(MetaFactory, Ctor) {
const std::array values{1, 3};
entt::meta_factory<clazz> factory{};
ASSERT_FALSE(entt::resolve<clazz>().construct(values[0u]));
ASSERT_FALSE(entt::resolve<clazz>().construct(std::to_string(values[1u])));
factory.ctor<int>().ctor<&clazz::from_string>();
const auto instance = entt::resolve<clazz>().construct(values[0u]);
const auto other = entt::resolve<clazz>().construct(std::to_string(values[1u]));
ASSERT_TRUE(instance);
ASSERT_TRUE(other);
ASSERT_TRUE(instance.allow_cast<clazz>());
ASSERT_TRUE(other.allow_cast<clazz>());
ASSERT_EQ(instance.cast<const clazz &>().get_int(), values[0u]);
ASSERT_EQ(other.cast<const clazz &>().get_int(), values[1u]);
}
TEST_F(MetaFactory, Dtor) {
bool check = false;
entt::meta_factory<dtor_callback> factory{};
entt::meta_any any{std::in_place_type<dtor_callback>, check};
any.reset();
ASSERT_FALSE(check);
factory.dtor<&dtor_callback::on_destroy>();
any.emplace<dtor_callback>(check);
any.reset();
ASSERT_TRUE(check);
}
TEST_F(MetaFactory, DataMemberObject) {
using namespace entt::literals;
base instance{'c'};
entt::meta_factory<base> factory{};
entt::meta_type type = entt::resolve<base>();
ASSERT_FALSE(type.data("member"_hs));
factory.data<&base::member>("member"_hs);
type = entt::resolve<base>();
ASSERT_TRUE(type.data("member"_hs));
ASSERT_EQ(type.get("member"_hs, std::as_const(instance)), instance.member);
ASSERT_EQ(type.get("member"_hs, instance), instance.member);
ASSERT_FALSE(type.set("member"_hs, std::as_const(instance), instance.member));
ASSERT_TRUE(type.set("member"_hs, instance, instance.member));
}
TEST_F(MetaFactory, DataPointer) {
using namespace entt::literals;
entt::meta_factory<int> factory{};
entt::meta_type type = entt::resolve<int>();
ASSERT_FALSE(type.data("value"_hs));
static int value = 1;
factory.data<&value>("value"_hs);
type = entt::resolve<int>();
ASSERT_TRUE(type.data("value"_hs));
ASSERT_EQ(type.get("value"_hs, {}), value);
ASSERT_TRUE(type.set("value"_hs, {}, value));
}
TEST_F(MetaFactory, DataValue) {
using namespace entt::literals;
constexpr int value = 1;
entt::meta_factory<int> factory{};
entt::meta_type type = entt::resolve<int>();
ASSERT_FALSE(type.data("value"_hs));
factory.data<value>("value"_hs);
type = entt::resolve<int>();
ASSERT_TRUE(type.data("value"_hs));
ASSERT_EQ(type.get("value"_hs, {}), value);
ASSERT_FALSE(type.set("value"_hs, {}, value));
}
TEST_F(MetaFactory, DataGetterOnly) {
using namespace entt::literals;
clazz instance{1};
entt::meta_factory<clazz> factory{};
entt::meta_type type = entt::resolve<clazz>();
ASSERT_FALSE(type.data("value"_hs));
factory.data<nullptr, &clazz::get_int>("value"_hs);
type = entt::resolve<clazz>();
ASSERT_TRUE(type.data("value"_hs));
ASSERT_EQ(type.get("value"_hs, std::as_const(instance)), instance.get_int());
ASSERT_EQ(type.get("value"_hs, instance), instance.get_int());
ASSERT_FALSE(type.set("value"_hs, std::as_const(instance), instance.get_int()));
ASSERT_FALSE(type.set("value"_hs, instance, instance.get_int()));
}
TEST_F(MetaFactory, DataSetterGetter) {
using namespace entt::literals;
clazz instance{1};
entt::meta_factory<clazz> factory{};
entt::meta_type type = entt::resolve<clazz>();
ASSERT_FALSE(type.data("value"_hs));
factory.data<&clazz::set_int, &clazz::get_int>("value"_hs);
type = entt::resolve<clazz>();
ASSERT_TRUE(type.data("value"_hs));
ASSERT_EQ(type.get("value"_hs, std::as_const(instance)), instance.get_int());
ASSERT_EQ(type.get("value"_hs, instance), instance.get_int());
ASSERT_FALSE(type.set("value"_hs, std::as_const(instance), instance.get_int()));
ASSERT_TRUE(type.set("value"_hs, instance, instance.get_int()));
}
TEST_F(MetaFactory, DataMultiSetterGetter) {
using namespace entt::literals;
clazz instance{1};
entt::meta_factory<clazz> factory{};
entt::meta_type type = entt::resolve<clazz>();
ASSERT_FALSE(type.data("value"_hs));
factory.data<entt::value_list<&clazz::set_int, &clazz::set_boxed_int>, &clazz::get_int>("value"_hs);
type = entt::resolve<clazz>();
ASSERT_TRUE(type.data("value"_hs));
ASSERT_EQ(type.get("value"_hs, std::as_const(instance)), instance.get_int());
ASSERT_EQ(type.get("value"_hs, instance), instance.get_int());
ASSERT_FALSE(type.set("value"_hs, std::as_const(instance), instance.get_int()));
ASSERT_TRUE(type.set("value"_hs, instance, instance.get_int()));
ASSERT_FALSE(type.set("value"_hs, std::as_const(instance), test::boxed_int{instance.get_int()}));
ASSERT_TRUE(type.set("value"_hs, instance, test::boxed_int{instance.get_int()}));
}
TEST_F(MetaFactory, DataOverwrite) {
using namespace entt::literals;
entt::meta_factory<clazz> factory{};
entt::meta_type type = entt::resolve<clazz>();
ASSERT_FALSE(type.data("value"_hs));
factory.data<nullptr, &clazz::get_int>("value"_hs);
type = entt::resolve<clazz>();
ASSERT_TRUE(type.data("value"_hs));
ASSERT_TRUE(type.data("value"_hs).is_const());
factory.data<&clazz::set_int, &clazz::get_int>("value"_hs);
type = entt::resolve<clazz>();
ASSERT_TRUE(type.data("value"_hs));
ASSERT_FALSE(type.data("value"_hs).is_const());
}
TEST_F(MetaFactory, Func) {
using namespace entt::literals;
const clazz instance{1};
entt::meta_factory<clazz> factory{};
entt::meta_type type = entt::resolve<clazz>();
ASSERT_FALSE(type.func("func"_hs));
factory.func<&clazz::get_int>("func"_hs);
type = entt::resolve<clazz>();
ASSERT_TRUE(type.func("func"_hs));
ASSERT_TRUE(type.invoke("func"_hs, instance));
ASSERT_EQ(type.invoke("func"_hs, instance).cast<int>(), instance.get_int());
ASSERT_FALSE(type.invoke("func"_hs, {}));
}
TEST_F(MetaFactory, FuncOverload) {
using namespace entt::literals;
clazz instance{1};
entt::meta_factory<clazz> factory{};
const entt::meta_type type = entt::resolve<clazz>();
ASSERT_FALSE(type.func("func"_hs));
factory.func<&clazz::set_int>("func"_hs);
ASSERT_TRUE(type.func("func"_hs));
ASSERT_FALSE(type.func("func"_hs).next());
factory.func<&clazz::set_boxed_int>("func"_hs);
ASSERT_TRUE(type.func("func"_hs));
ASSERT_TRUE(type.func("func"_hs).next());
ASSERT_FALSE(type.func("func"_hs).next().next());
ASSERT_TRUE(type.invoke("func"_hs, instance, 2));
ASSERT_EQ(instance.get_int(), 2);
ASSERT_TRUE(type.invoke("func"_hs, instance, test::boxed_int{3}));
ASSERT_EQ(instance.get_int(), 3);
}
TEST_F(MetaFactory, Traits) {
using namespace entt::literals;
entt::meta_factory<clazz>{}
.data<&base::member>("member"_hs)
.func<&clazz::set_int>("func"_hs)
.func<&clazz::set_boxed_int>("func"_hs);
entt::meta_type type = entt::resolve<clazz>();
ASSERT_EQ(type.traits<test::meta_traits>(), test::meta_traits::none);
ASSERT_EQ(type.data("member"_hs).traits<test::meta_traits>(), test::meta_traits::none);
ASSERT_EQ(type.func("func"_hs).traits<test::meta_traits>(), test::meta_traits::none);
ASSERT_EQ(type.func("func"_hs).next().traits<test::meta_traits>(), test::meta_traits::none);
entt::meta_factory<clazz>{}
.traits(test::meta_traits::one | test::meta_traits::three)
.data<&base::member>("member"_hs)
.traits(test::meta_traits::one)
.func<&clazz::set_int>("func"_hs)
.traits(test::meta_traits::two)
.func<&clazz::set_boxed_int>("func"_hs)
.traits(test::meta_traits::three);
// traits are copied and never refreshed
type = entt::resolve<clazz>();
ASSERT_EQ(type.traits<test::meta_traits>(), test::meta_traits::one | test::meta_traits::three);
ASSERT_EQ(type.data("member"_hs).traits<test::meta_traits>(), test::meta_traits::one);
ASSERT_EQ(type.func("func"_hs).traits<test::meta_traits>(), test::meta_traits::two);
ASSERT_EQ(type.func("func"_hs).next().traits<test::meta_traits>(), test::meta_traits::three);
}
TEST_F(MetaFactory, Custom) {
using namespace entt::literals;
entt::meta_factory<clazz>{}
.data<&base::member>("member"_hs)
.func<&clazz::set_int>("func"_hs)
.func<&clazz::set_boxed_int>("func"_hs);
entt::meta_type type = entt::resolve<clazz>();
ASSERT_EQ(static_cast<const int *>(type.custom()), nullptr);
ASSERT_EQ(static_cast<const int *>(type.data("member"_hs).custom()), nullptr);
ASSERT_EQ(static_cast<const int *>(type.func("func"_hs).custom()), nullptr);
ASSERT_EQ(static_cast<const int *>(type.func("func"_hs).next().custom()), nullptr);
entt::meta_factory<clazz>{}
.custom<int>(0)
.data<&base::member>("member"_hs)
.custom<int>(1)
.func<&clazz::set_int>("func"_hs)
.custom<int>(2)
.func<&clazz::set_boxed_int>("func"_hs)
.custom<int>(3);
// custom data pointers are copied and never refreshed
type = entt::resolve<clazz>();
ASSERT_EQ(static_cast<int>(type.custom()), 0);
ASSERT_EQ(static_cast<int>(type.data("member"_hs).custom()), 1);
ASSERT_EQ(static_cast<int>(type.func("func"_hs).custom()), 2);
ASSERT_EQ(static_cast<int>(type.func("func"_hs).next().custom()), 3);
}
TEST_F(MetaFactory, Meta) {
entt::meta_ctx ctx{};
ASSERT_EQ(entt::resolve(entt::type_id<int>()), entt::meta_type{});
ASSERT_EQ(entt::resolve(ctx, entt::type_id<int>()), entt::meta_type{});
auto factory = entt::meta<int>();
ASSERT_NE(entt::resolve(entt::type_id<int>()), entt::meta_type{});
ASSERT_EQ(entt::resolve(ctx, entt::type_id<int>()), entt::meta_type{});
ASSERT_TRUE(entt::resolve(entt::type_id<int>()).is_integral());
factory = entt::meta<int>(ctx);
ASSERT_NE(entt::resolve(entt::type_id<int>()), entt::meta_type{});
ASSERT_NE(entt::resolve(ctx, entt::type_id<int>()), entt::meta_type{});
ASSERT_TRUE(entt::resolve(ctx, entt::type_id<int>()).is_integral());
}
TEST_F(MetaFactory, MetaReset) {
using namespace entt::literals;
entt::meta_ctx ctx{};
entt::meta_factory<int>{}.type("global"_hs);
entt::meta_factory<int>{ctx}.type("local"_hs);
ASSERT_TRUE(entt::resolve(entt::type_id<int>()));
ASSERT_TRUE(entt::resolve(ctx, entt::type_id<int>()));
entt::meta_reset();
ASSERT_FALSE(entt::resolve(entt::type_id<int>()));
ASSERT_TRUE(entt::resolve(ctx, entt::type_id<int>()));
entt::meta_reset(ctx);
ASSERT_FALSE(entt::resolve(entt::type_id<int>()));
ASSERT_FALSE(entt::resolve(ctx, entt::type_id<int>()));
entt::meta_factory<int>{}.type("global"_hs);
entt::meta_factory<int>{ctx}.type("local"_hs);
ASSERT_TRUE(entt::resolve(entt::type_id<int>()));
ASSERT_TRUE(entt::resolve(ctx, entt::type_id<int>()));
entt::meta_reset<int>();
ASSERT_FALSE(entt::resolve(entt::type_id<int>()));
ASSERT_TRUE(entt::resolve(ctx, entt::type_id<int>()));
entt::meta_reset<int>(ctx);
ASSERT_FALSE(entt::resolve(entt::type_id<int>()));
ASSERT_FALSE(entt::resolve(ctx, entt::type_id<int>()));
entt::meta_factory<int>{}.type("global"_hs);
entt::meta_factory<int>{ctx}.type("local"_hs);
ASSERT_TRUE(entt::resolve(entt::type_id<int>()));
ASSERT_TRUE(entt::resolve(ctx, entt::type_id<int>()));
entt::meta_reset("global"_hs);
ASSERT_FALSE(entt::resolve(entt::type_id<int>()));
ASSERT_TRUE(entt::resolve(ctx, entt::type_id<int>()));
entt::meta_reset(ctx, "local"_hs);
ASSERT_FALSE(entt::resolve(entt::type_id<int>()));
ASSERT_FALSE(entt::resolve(ctx, entt::type_id<int>()));
}

View File

@@ -0,0 +1,701 @@
#include <cstddef>
#include <type_traits>
#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/policy.hpp>
#include <entt/meta/range.hpp>
#include <entt/meta/resolve.hpp>
#include "../../common/config.h"
#include "../../common/meta_traits.h"
struct base {
base() = default;
virtual ~base() = default;
void setter(int iv) {
value = iv;
}
[[nodiscard]] int getter() const {
return value;
}
static void static_setter(base &ref, int iv) {
ref.value = iv;
}
int value{3};
};
void fake_member(base &instance, int value) {
instance.value = value;
}
[[nodiscard]] int fake_const_member(const base &instance) {
return instance.value;
}
struct derived: base {
derived() = default;
};
struct function {
[[nodiscard]] int f(const base &, int val, int other) {
return f(val, other);
}
[[nodiscard]] int f(int val, const int other) {
value = val;
return other * other;
}
[[nodiscard]] int f(int iv) const {
return value * iv;
}
void g(int iv) {
value = iv * iv;
}
[[nodiscard]] static int h(int &iv, const function &instance) {
return (iv *= instance.value);
}
static void k(int iv, function &instance) {
instance.value = iv;
}
[[nodiscard]] int v(int &iv) const {
return (iv = value);
}
[[nodiscard]] int &a() {
return value;
}
[[nodiscard]] operator int() const {
return value;
}
int value{};
};
double double_member(const double &value) {
return value * value;
}
struct MetaFunc: ::testing::Test {
void SetUp() override {
using namespace entt::literals;
entt::meta_factory<double>{}
.type("double"_hs)
.func<&double_member>("member"_hs);
entt::meta_factory<base>{}
.type("base"_hs)
.func<&base::setter>("setter"_hs)
.func<fake_member>("fake_member"_hs)
.func<fake_const_member>("fake_const_member"_hs);
entt::meta_factory<derived>{}
.type("derived"_hs)
.base<base>()
.func<&base::setter>("setter_from_base"_hs)
.func<&base::getter>("getter_from_base"_hs)
.func<&base::static_setter>("static_setter_from_base"_hs);
entt::meta_factory<function>{}
.type("func"_hs)
.func<&entt::registry::emplace_or_replace<function>, entt::as_ref_t>("emplace"_hs)
.traits(test::meta_traits::one | test::meta_traits::two | test::meta_traits::three)
.func<entt::overload<int(const base &, int, int)>(&function::f)>("f3"_hs)
.traits(test::meta_traits::three)
.func<entt::overload<int(int, int)>(&function::f)>("f2"_hs)
.traits(test::meta_traits::two)
.custom<int>(2)
.func<entt::overload<int(int) const>(&function::f)>("f1"_hs)
.traits(test::meta_traits::one)
.func<&function::g>("g"_hs)
.custom<char>('c')
.func<function::h>("h"_hs)
.func<function::k>("k"_hs)
.func<&function::v, entt::as_void_t>("v"_hs)
.func<&function::a, entt::as_ref_t>("a"_hs)
.func<&function::a, entt::as_cref_t>("ca"_hs)
.conv<int>();
}
void TearDown() override {
entt::meta_reset();
}
std::size_t reset_and_check() {
std::size_t count = 0;
for(const auto &func: entt::resolve<function>().func()) {
for(auto curr = func.second; curr; curr = curr.next()) {
++count;
}
}
SetUp();
for(const auto &func: entt::resolve<function>().func()) {
for(auto curr = func.second; curr; curr = curr.next()) {
--count;
}
}
return count;
};
};
using MetaFuncDeathTest = MetaFunc;
TEST_F(MetaFunc, SafeWhenEmpty) {
const entt::meta_func func{};
entt::meta_any *args = nullptr;
ASSERT_FALSE(func);
ASSERT_EQ(func, entt::meta_func{});
ASSERT_EQ(func.arity(), 0u);
ASSERT_FALSE(func.is_const());
ASSERT_FALSE(func.is_static());
ASSERT_EQ(func.ret(), entt::meta_type{});
ASSERT_EQ(func.arg(0u), entt::meta_type{});
ASSERT_EQ(func.arg(1u), entt::meta_type{});
ASSERT_FALSE(func.invoke({}, args, 0u));
ASSERT_FALSE(func.invoke({}, args, 1u));
ASSERT_FALSE(func.invoke({}));
ASSERT_FALSE(func.invoke({}, 'c'));
ASSERT_EQ(func.traits<test::meta_traits>(), test::meta_traits::none);
ASSERT_EQ(static_cast<const char *>(func.custom()), nullptr);
ASSERT_EQ(func.next(), func);
}
TEST_F(MetaFunc, UserTraits) {
using namespace entt::literals;
ASSERT_EQ(entt::resolve<function>().func("h"_hs).traits<test::meta_traits>(), test::meta_traits::none);
ASSERT_EQ(entt::resolve<function>().func("k"_hs).traits<test::meta_traits>(), test::meta_traits::none);
ASSERT_EQ(entt::resolve<function>().func("emplace"_hs).traits<test::meta_traits>(), test::meta_traits::one | test::meta_traits::two | test::meta_traits::three);
ASSERT_EQ(entt::resolve<function>().func("f1"_hs).traits<test::meta_traits>(), test::meta_traits::one);
ASSERT_EQ(entt::resolve<function>().func("f2"_hs).traits<test::meta_traits>(), test::meta_traits::two);
ASSERT_EQ(entt::resolve<function>().func("f3"_hs).traits<test::meta_traits>(), test::meta_traits::three);
}
ENTT_DEBUG_TEST_F(MetaFuncDeathTest, UserTraits) {
using namespace entt::literals;
using traits_type = entt::internal::meta_traits;
constexpr auto value = traits_type{static_cast<std::underlying_type_t<traits_type>>(traits_type::_user_defined_traits) + 1u};
ASSERT_DEATH(entt::meta_factory<function>{}.func<&function::g>("g"_hs).traits(value), "");
}
TEST_F(MetaFunc, Custom) {
using namespace entt::literals;
ASSERT_EQ(*static_cast<const char *>(entt::resolve<function>().func("g"_hs).custom()), 'c');
ASSERT_EQ(static_cast<const char &>(entt::resolve<function>().func("g"_hs).custom()), 'c');
ASSERT_EQ(static_cast<const int *>(entt::resolve<function>().func("g"_hs).custom()), nullptr);
ASSERT_EQ(static_cast<const int *>(entt::resolve<function>().func("h"_hs).custom()), nullptr);
}
ENTT_DEBUG_TEST_F(MetaFuncDeathTest, Custom) {
using namespace entt::literals;
ASSERT_DEATH([[maybe_unused]] const int value = entt::resolve<function>().func("g"_hs).custom(), "");
ASSERT_DEATH([[maybe_unused]] const char value = entt::resolve<function>().func("h"_hs).custom(), "");
}
TEST_F(MetaFunc, Comparison) {
using namespace entt::literals;
auto func = entt::resolve<function>().func("f2"_hs);
ASSERT_TRUE(func);
ASSERT_EQ(func, func);
ASSERT_NE(func, entt::meta_func{});
ASSERT_FALSE(func != func);
ASSERT_TRUE(func == func);
}
TEST_F(MetaFunc, NonConst) {
using namespace entt::literals;
auto func = entt::resolve<function>().func("f2"_hs);
function 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(instance.value, 3);
}
TEST_F(MetaFunc, Const) {
using namespace entt::literals;
auto func = entt::resolve<function>().func("f1"_hs);
function instance{2};
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{});
ASSERT_FALSE(empty);
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<int>());
ASSERT_EQ(any.cast<int>(), 8);
}
TEST_F(MetaFunc, RetVoid) {
using namespace entt::literals;
auto func = entt::resolve<function>().func("g"_hs);
function 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, 4);
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<void>());
ASSERT_EQ(instance.value, 16);
}
TEST_F(MetaFunc, Static) {
using namespace entt::literals;
auto func = entt::resolve<function>().func("h"_hs);
function instance{2};
ASSERT_TRUE(func);
ASSERT_EQ(func.arity(), 2u);
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_EQ(func.arg(1u), entt::resolve<function>());
ASSERT_FALSE(func.arg(2u));
auto any = func.invoke({}, 3, entt::forward_as_meta(instance));
auto empty = func.invoke({}, derived{}, entt::forward_as_meta(instance));
ASSERT_FALSE(empty);
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<int>());
ASSERT_EQ(any.cast<int>(), 6);
}
TEST_F(MetaFunc, StaticRetVoid) {
using namespace entt::literals;
auto func = entt::resolve<function>().func("k"_hs);
function instance{};
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<int>());
ASSERT_EQ(func.arg(1u), entt::resolve<function>());
ASSERT_FALSE(func.arg(2u));
auto any = func.invoke({}, 3, entt::forward_as_meta(instance));
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<void>());
ASSERT_EQ(instance.value, 3);
}
TEST_F(MetaFunc, StaticAsMember) {
using namespace entt::literals;
base instance{};
auto func = entt::resolve<base>().func("fake_member"_hs);
auto any = func.invoke(instance, 3);
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_FALSE(func.invoke({}, 3));
ASSERT_FALSE(func.invoke(std::as_const(instance), 3));
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<void>());
ASSERT_EQ(instance.value, 3);
}
TEST_F(MetaFunc, StaticAsConstMember) {
using namespace entt::literals;
base instance{};
auto func = entt::resolve<base>().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_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, NonClassTypeMember) {
using namespace entt::literals;
double instance = 3.;
auto func = entt::resolve<double>().func("member"_hs);
auto any = func.invoke(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<double>());
ASSERT_FALSE(func.arg(0u));
ASSERT_FALSE(func.invoke({}));
ASSERT_TRUE(func.invoke(instance));
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<double>());
ASSERT_EQ(any.cast<double>(), instance * instance);
}
TEST_F(MetaFunc, MetaAnyArgs) {
using namespace entt::literals;
function instance{3};
auto any = entt::resolve<function>().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<function>().func("f1"_hs).invoke(value, 'c'));
}
TEST_F(MetaFunc, CastAndConvert) {
using namespace entt::literals;
function instance;
instance.value = 3;
auto any = entt::resolve<function>().func("f3"_hs).invoke(instance, derived{}, 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;
function instance;
auto any = entt::resolve<function>().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<function>().func("h"_hs);
function instance{2};
entt::meta_any any{3};
int value = 4;
ASSERT_EQ(func.invoke({}, entt::forward_as_meta(value), entt::forward_as_meta(instance)).cast<int>(), 8);
ASSERT_EQ(func.invoke({}, any.as_ref(), entt::forward_as_meta(instance)).cast<int>(), 6);
ASSERT_EQ(any.cast<int>(), 6);
ASSERT_EQ(value, 8);
}
TEST_F(MetaFunc, ArgsByConstRef) {
using namespace entt::literals;
function instance{};
auto func = entt::resolve<function>().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(instance.value, 9);
ASSERT_TRUE(func.invoke(instance, std::as_const(any).as_ref()));
ASSERT_EQ(instance.value, 4);
}
TEST_F(MetaFunc, ConstInstance) {
using namespace entt::literals;
function instance{2};
auto any = entt::resolve<function>().func("f1"_hs).invoke(std::as_const(instance), 2);
ASSERT_FALSE(entt::resolve<function>().func("g"_hs).invoke(std::as_const(instance), 1));
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<int>(), 4);
}
TEST_F(MetaFunc, AsVoid) {
using namespace entt::literals;
auto func = entt::resolve<function>().func("v"_hs);
function instance{3};
int value{2};
ASSERT_EQ(func.invoke(instance, entt::forward_as_meta(value)), entt::meta_any{std::in_place_type<void>});
ASSERT_EQ(func.ret(), entt::resolve<void>());
ASSERT_EQ(value, instance.value);
}
TEST_F(MetaFunc, AsRef) {
using namespace entt::literals;
function instance{};
auto func = entt::resolve<function>().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;
function instance{3};
auto func = entt::resolve<function>().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;
function instance{};
auto func = entt::resolve<function>().func("ca"_hs);
ASSERT_DEATH((func.invoke(instance).cast<int &>() = 3), "");
}
TEST_F(MetaFunc, InvokeBaseFunction) {
using namespace entt::literals;
auto type = entt::resolve<derived>();
derived instance{};
ASSERT_TRUE(type.func("setter"_hs));
ASSERT_EQ(instance.value, 3);
type.func("setter"_hs).invoke(instance, 1);
ASSERT_EQ(instance.value, 1);
}
TEST_F(MetaFunc, InvokeFromBase) {
using namespace entt::literals;
auto type = entt::resolve<derived>();
derived 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, 1);
ASSERT_EQ(instance.value, 1);
auto getter_from_base = type.func("getter_from_base"_hs);
ASSERT_TRUE(getter_from_base);
ASSERT_EQ(getter_from_base.invoke(instance).cast<int>(), 1);
auto static_setter_from_base = type.func("static_setter_from_base"_hs);
ASSERT_TRUE(static_setter_from_base);
ASSERT_EQ(instance.value, 1);
static_setter_from_base.invoke(instance, 3);
ASSERT_EQ(instance.value, 3);
}
TEST_F(MetaFunc, ExternalMemberFunction) {
using namespace entt::literals;
auto func = entt::resolve<function>().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<function>());
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<function>(entity));
func.invoke({}, entt::forward_as_meta(registry), entity);
ASSERT_TRUE(registry.all_of<function>(entity));
}
TEST_F(MetaFunc, Overloaded) {
using namespace entt::literals;
auto type = entt::resolve<function>();
ASSERT_FALSE(type.func("f2"_hs).next());
entt::meta_factory<function>{}
// this should not overwrite traits and custom data
.func<entt::overload<int(int, int)>(&function::f)>("f2"_hs)
// this should put traits and custom data on the new overload instead
.func<entt::overload<int(int) const>(&function::f)>("f2"_hs)
.traits(test::meta_traits::three)
.custom<int>(3);
ASSERT_TRUE(type.func("f2"_hs).next());
ASSERT_FALSE(type.func("f2"_hs).next().next());
ASSERT_EQ(type.func("f2"_hs).traits<test::meta_traits>(), test::meta_traits::two);
ASSERT_EQ(type.func("f2"_hs).next().traits<test::meta_traits>(), test::meta_traits::three);
ASSERT_NE(static_cast<const int *>(type.func("f2"_hs).custom()), nullptr);
ASSERT_NE(static_cast<const int *>(type.func("f2"_hs).next().custom()), nullptr);
ASSERT_EQ(static_cast<const int &>(type.func("f2"_hs).custom()), 2);
ASSERT_EQ(static_cast<const int &>(type.func("f2"_hs).next().custom()), 3);
}
TEST_F(MetaFunc, OverloadedOrder) {
using namespace entt::literals;
entt::meta_factory<function>{}
.func<entt::overload<int(int, int)>(&function::f)>("f2"_hs)
.func<entt::overload<int(int) const>(&function::f)>("f2"_hs);
auto type = entt::resolve<function>();
auto func = type.func("f2"_hs);
ASSERT_TRUE(func);
ASSERT_EQ(func.arity(), 2u);
ASSERT_FALSE(func.is_const());
ASSERT_EQ(func.ret(), entt::resolve<int>());
func = func.next();
ASSERT_TRUE(func);
ASSERT_EQ(func.arity(), 1u);
ASSERT_TRUE(func.is_const());
ASSERT_EQ(func.ret(), entt::resolve<int>());
func = func.next();
ASSERT_FALSE(func);
}
TEST_F(MetaFunc, ReRegistration) {
using namespace entt::literals;
ASSERT_EQ(reset_and_check(), 0u);
function instance{};
auto type = entt::resolve<function>();
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_factory<function>{}
.func<entt::overload<int(int, int)>(&function::f)>("f"_hs)
.func<entt::overload<int(int) const>(&function::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));
entt::meta_factory<function>{}
.func<entt::overload<int(int, int)>(&function::f)>("f"_hs)
.traits(test::meta_traits::one)
.custom<int>(3)
// this should not overwrite traits and custom data
.func<entt::overload<int(int, int)>(&function::f)>("f"_hs);
ASSERT_EQ(type.func("f"_hs).traits<test::meta_traits>(), test::meta_traits::one);
ASSERT_NE(static_cast<const int *>(type.func("f"_hs).custom()), nullptr);
ASSERT_EQ(reset_and_check(), 0u);
}

View File

@@ -0,0 +1,73 @@
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
struct clazz {
void incr() {
++value;
}
void decr() {
--value;
}
int value{};
};
struct MetaHandle: ::testing::Test {
void SetUp() override {
using namespace entt::literals;
entt::meta_factory<clazz>{}
.type("clazz"_hs)
.func<&clazz::incr>("incr"_hs)
.func<&clazz::decr>("decr"_hs);
}
void TearDown() override {
entt::meta_reset();
}
};
TEST_F(MetaHandle, Handle) {
using namespace entt::literals;
clazz instance{};
entt::meta_handle handle{};
entt::meta_handle chandle{};
ASSERT_FALSE(handle);
ASSERT_FALSE(chandle);
ASSERT_EQ(handle, chandle);
ASSERT_EQ(handle, entt::meta_handle{});
ASSERT_FALSE(handle != handle);
ASSERT_TRUE(handle == handle);
handle = entt::meta_handle{instance};
chandle = entt::meta_handle{std::as_const(instance)};
ASSERT_TRUE(handle);
ASSERT_TRUE(chandle);
ASSERT_EQ(handle, chandle);
ASSERT_NE(handle, entt::meta_handle{});
ASSERT_FALSE(handle != handle);
ASSERT_TRUE(handle == handle);
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,462 @@
#include <memory>
#include <type_traits>
#include <utility>
#include <gtest/gtest.h>
#include <entt/meta/adl_pointer.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/pointer.hpp>
#include <entt/meta/resolve.hpp>
#include <entt/meta/type_traits.hpp>
#include "../../common/config.h"
template<typename Type>
struct wrapped_shared_ptr {
wrapped_shared_ptr(Type init)
: ptr{new Type{init}} {}
[[nodiscard]] Type &deref() const {
return *ptr;
}
private:
std::shared_ptr<Type> ptr;
};
struct self_ptr {
using element_type = self_ptr;
self_ptr(int val)
: value{val} {}
const self_ptr &operator*() const {
return *this;
}
int value;
};
struct proxy_ptr {
using element_type = proxy_ptr;
proxy_ptr(int &val)
: value{&val} {}
proxy_ptr operator*() const {
return *this;
}
int *value;
};
template<typename Type>
struct adl_wrapped_shared_ptr: wrapped_shared_ptr<Type> {
using is_meta_pointer_like = void;
};
template<typename Type>
struct spec_wrapped_shared_ptr: wrapped_shared_ptr<Type> {
using is_meta_pointer_like = void;
};
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 3;
}
TEST(MetaPointerLike, DereferenceOperatorInvalidType) {
const int value = 0;
const 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 = 3;
const 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 &>(), 3);
}
ENTT_DEBUG_TEST(MetaPointerLikeDeathTest, DereferenceOperatorConstType) {
const int value = 3;
const entt::meta_any any{&value};
auto deref = *any;
ASSERT_TRUE(deref);
ASSERT_DEATH(deref.cast<int &>() = 0, "");
}
TEST(MetaPointerLike, DereferenceOperatorConstAnyNonConstType) {
int value = 3;
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 &>(), 3);
ASSERT_EQ(deref.cast<const int &>(), 3);
}
TEST(MetaPointerLike, DereferenceOperatorConstAnyConstType) {
const int value = 3;
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 &>(), 3);
}
ENTT_DEBUG_TEST(MetaPointerLikeDeathTest, DereferenceOperatorConstAnyConstType) {
const int value = 3;
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 &>() = 3;
ASSERT_EQ(*any.cast<int *>(), 3);
ASSERT_EQ(value, 3);
}
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 &>() = 3;
ASSERT_EQ(*any.cast<std::shared_ptr<int>>(), 3);
ASSERT_EQ(*value, 3);
}
TEST(MetaPointerLike, PointerToConstMoveOnlyType) {
const std::unique_ptr<int> instance;
const entt::meta_any any{&instance};
auto deref = *any;
ASSERT_TRUE(any);
ASSERT_TRUE(deref);
ASSERT_EQ(deref.try_cast<std::unique_ptr<int>>(), nullptr);
ASSERT_NE(deref.try_cast<const std::unique_ptr<int>>(), nullptr);
ASSERT_EQ(&deref.cast<const std::unique_ptr<int> &>(), &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 &>() = 3;
ASSERT_EQ(*any.cast<int *>(), 3);
ASSERT_EQ(value, 3);
}
TEST(MetaPointerLike, AsConstRef) {
int value = 3;
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 &>() = 3;
ASSERT_EQ(*any.cast<int *>(), 3);
ASSERT_EQ(value, 3);
}
TEST(MetaPointerLike, DereferenceOverloadAdl) {
const entt::meta_any any{adl_wrapped_shared_ptr<int>{3}};
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 &>(), 3);
ASSERT_EQ(deref.cast<const int &>(), 3);
}
TEST(MetaPointerLike, DereferenceOverloadSpec) {
const entt::meta_any any{spec_wrapped_shared_ptr<int>{3}};
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 &>(), 3);
ASSERT_EQ(deref.cast<const int &>(), 3);
}
TEST(MetaPointerLike, DereferencePointerToConstOverloadAdl) {
const entt::meta_any any{adl_wrapped_shared_ptr<const int>{3}};
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 &>(), 3);
}
TEST(MetaPointerLike, DereferencePointerToConstOverloadSpec) {
const entt::meta_any any{spec_wrapped_shared_ptr<const int>{3}};
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 &>(), 3);
}
ENTT_DEBUG_TEST(MetaPointerLikeDeathTest, DereferencePointerToConstOverloadAdl) {
const entt::meta_any any{adl_wrapped_shared_ptr<const int>{3}};
auto deref = *any;
ASSERT_TRUE(deref);
ASSERT_DEATH(deref.cast<int &>() = 3, "");
}
ENTT_DEBUG_TEST(MetaPointerLikeDeathTest, DereferencePointerToConstOverloadSpec) {
const entt::meta_any any{spec_wrapped_shared_ptr<const int>{3}};
auto deref = *any;
ASSERT_TRUE(deref);
ASSERT_DEATH(deref.cast<int &>() = 3, "");
}
TEST(MetaPointerLike, DereferencePointerToVoid) {
const entt::meta_any any{static_cast<void *>(nullptr)};
ASSERT_TRUE(any.type().is_pointer());
ASSERT_TRUE(any.type().is_pointer_like());
auto deref = *any;
ASSERT_FALSE(deref);
}
TEST(MetaPointerLike, DereferencePointerToConstVoid) {
const entt::meta_any any{static_cast<const void *>(nullptr)};
ASSERT_TRUE(any.type().is_pointer());
ASSERT_TRUE(any.type().is_pointer_like());
auto deref = *any;
ASSERT_FALSE(deref);
}
TEST(MetaPointerLike, DereferenceSharedPointerToVoid) {
const entt::meta_any any{std::shared_ptr<void>{}};
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(MetaPointerLike, DereferenceUniquePointerToVoid) {
const entt::meta_any any{std::unique_ptr<void, void (*)(void *)>{nullptr, nullptr}};
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(MetaPointerLike, DereferencePointerToFunction) {
entt::meta_any any{&test_function};
ASSERT_TRUE(any.type().is_pointer());
ASSERT_TRUE((*std::as_const(any)).type().is_pointer_like());
ASSERT_NE((**any).try_cast<int (*)()>(), nullptr);
ASSERT_EQ((***std::as_const(any)).cast<int (*)()>()(), 3);
}
TEST(MetaPointerLike, DereferenceSelfPointer) {
self_ptr obj{3};
const 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;
const proxy_ptr obj{value};
const 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 = 3;
ASSERT_EQ(value, 3);
}
TEST(MetaPointerLike, DereferenceArray) {
// NOLINTBEGIN(*-avoid-c-arrays)
const entt::meta_any array{std::in_place_type<int[3]>};
const entt::meta_any array_of_array{std::in_place_type<int[3][3]>};
// NOLINTEND(*-avoid-c-arrays)
// NOLINTBEGIN(*-avoid-c-arrays)
ASSERT_EQ(array.type(), entt::resolve<int[3]>());
ASSERT_EQ(array_of_array.type(), entt::resolve<int[3][3]>());
// NOLINTEND(*-avoid-c-arrays)
ASSERT_FALSE(*array);
ASSERT_FALSE(*array_of_array);
}
TEST(MetaPointerLike, DereferencePlainNullPointer) {
const entt::meta_any any{static_cast<int *>(nullptr)};
ASSERT_TRUE(any);
ASSERT_FALSE(*any);
}
TEST(MetaPointerLike, DereferenceSharedNullPointer) {
const entt::meta_any any{std::shared_ptr<int>{}};
ASSERT_TRUE(any);
ASSERT_FALSE(*any);
}
TEST(MetaPointerLike, DereferenceUniqueNullPointer) {
const entt::meta_any any{std::unique_ptr<int>{}};
ASSERT_TRUE(any);
ASSERT_FALSE(*any);
}

View File

@@ -0,0 +1,98 @@
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/core/iterator.hpp>
#include <entt/core/type_info.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_factory<int>{}
.type("int"_hs)
.data<2>("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;
testing::StaticAssertTypeEq<iterator::value_type, std::pair<entt::id_type, entt::meta_type>>();
testing::StaticAssertTypeEq<iterator::pointer, entt::input_iterator_pointer<std::pair<entt::id_type, entt::meta_type>>>();
testing::StaticAssertTypeEq<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_factory<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>(), 2);
}
}

View File

@@ -0,0 +1,50 @@
#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>
#include <entt/meta/type_traits.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,905 @@
#include <algorithm>
#include <cstdint>
#include <map>
#include <memory>
#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/range.hpp>
#include <entt/meta/resolve.hpp>
#include <entt/meta/template.hpp>
#include "../../common/config.h"
#include "../../common/meta_traits.h"
template<typename Type>
void set(Type &elem, Type value) {
elem = value;
}
template<typename Type>
Type get(Type &elem) {
return elem;
}
struct base {
char value{'c'};
};
struct derived: base {
derived()
: base{} {}
};
struct abstract {
virtual ~abstract() = default;
virtual void func(int) {}
void base_only(int) {}
};
struct concrete: base, abstract {
void func(int val) override {
abstract::func(val);
value = val;
}
int value{3};
};
struct clazz {
clazz() = default;
clazz(const base &, int val)
: value{val} {}
void member() {}
static void func() {}
[[nodiscard]] operator int() const {
return value;
}
int value{};
};
struct overloaded_func {
[[nodiscard]] int f(const base &, int first, int second) {
return f(first, second);
}
[[nodiscard]] int f(int first, const int second) {
value = first;
return second * second;
}
[[nodiscard]] int f(int val) {
return 2 * std::as_const(*this).f(val);
}
[[nodiscard]] int f(int val) const {
return val * value;
}
[[nodiscard]] float f(int first, const float second) {
value = first;
return second + second;
}
int value{};
};
struct from_void_callback {
from_void_callback(bool &ref)
: cb{&ref} {}
from_void_callback(const from_void_callback &) = delete;
from_void_callback &operator=(const from_void_callback &) = delete;
~from_void_callback() {
*cb = !*cb;
}
private:
bool *cb;
};
enum class property_type : std::uint8_t {
value,
other
};
struct MetaType: ::testing::Test {
void SetUp() override {
using namespace entt::literals;
entt::meta_factory<double>{}
.type("double"_hs)
.traits(test::meta_traits::one)
.data<set<double>, get<double>>("var"_hs);
entt::meta_factory<unsigned int>{}
.type("unsigned int"_hs)
.traits(test::meta_traits::two)
.data<0u>("min"_hs)
.data<128u>("max"_hs);
entt::meta_factory<base>{}
.type("base"_hs)
.data<&base::value>("value"_hs);
entt::meta_factory<derived>{}
.type("derived"_hs)
.traits(test::meta_traits::one | test::meta_traits::three)
.base<base>();
entt::meta_factory<abstract>{}
.type("abstract"_hs)
.func<&abstract::func>("func"_hs)
.func<&abstract::base_only>("base_only"_hs);
entt::meta_factory<concrete>{}
.type("concrete"_hs)
.base<base>()
.base<abstract>();
entt::meta_factory<overloaded_func>{}
.type("overloaded_func"_hs)
.func<entt::overload<int(const base &, int, int)>(&overloaded_func::f)>("f"_hs)
.func<entt::overload<int(int, int)>(&overloaded_func::f)>("f"_hs)
.func<entt::overload<int(int)>(&overloaded_func::f)>("f"_hs)
.func<entt::overload<int(int) const>(&overloaded_func::f)>("f"_hs)
.func<entt::overload<float(int, float)>(&overloaded_func::f)>("f"_hs);
entt::meta_factory<property_type>{}
.type("property"_hs)
.traits(test::meta_traits::two | test::meta_traits::three)
.data<property_type::value>("value"_hs)
.data<property_type::other>("other"_hs)
.data<set<property_type>, get<property_type>>("var"_hs);
entt::meta_factory<clazz>{}
.type("class"_hs)
.custom<char>('c')
.ctor<const base &, int>()
.data<&clazz::value>("value"_hs)
.func<&clazz::member>("member"_hs)
.func<clazz::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();
const auto it = std::find_if(range.begin(), range.end(), [](auto curr) { return curr.second.id() == "class"_hs; });
ASSERT_NE(it, range.end());
ASSERT_EQ(it->second, entt::resolve<clazz>());
bool found = false;
for(auto curr: entt::resolve()) {
found = found || curr.second == entt::resolve<double>();
}
ASSERT_TRUE(found);
}
TEST_F(MetaType, SafeWhenEmpty) {
using namespace entt::literals;
entt::meta_type type{};
entt::meta_any *args = nullptr;
ASSERT_FALSE(type);
ASSERT_EQ(type, entt::meta_type{});
ASSERT_EQ(type.info(), entt::type_id<void>());
ASSERT_EQ(type.id(), entt::id_type{});
ASSERT_EQ(type.size_of(), 0u);
ASSERT_FALSE(type.is_arithmetic());
ASSERT_FALSE(type.is_integral());
ASSERT_FALSE(type.is_signed());
ASSERT_FALSE(type.is_array());
ASSERT_FALSE(type.is_enum());
ASSERT_FALSE(type.is_class());
ASSERT_FALSE(type.is_pointer());
ASSERT_EQ(type.remove_pointer(), type);
ASSERT_FALSE(type.is_pointer_like());
ASSERT_FALSE(type.is_sequence_container());
ASSERT_FALSE(type.is_associative_container());
ASSERT_FALSE(type.is_template_specialization());
ASSERT_EQ(type.template_arity(), 0u);
ASSERT_EQ(type.template_type(), type);
ASSERT_EQ(type.template_arg(0u), type);
ASSERT_EQ(type.template_arg(1u), type);
ASSERT_FALSE(type.can_cast(type));
ASSERT_FALSE(type.can_cast(entt::resolve<void>()));
ASSERT_FALSE(type.can_convert(type));
ASSERT_FALSE(type.can_convert(entt::resolve<void>()));
ASSERT_EQ(type.base().begin(), type.base().end());
ASSERT_EQ(type.data().begin(), type.data().end());
ASSERT_EQ(type.data("data"_hs), entt::meta_data{});
ASSERT_EQ(type.func().begin(), type.func().end());
ASSERT_EQ(type.func("func"_hs), entt::meta_func{});
ASSERT_FALSE(type.construct(args, 0u));
ASSERT_FALSE(type.construct(args, 1u));
ASSERT_FALSE(type.construct());
ASSERT_FALSE(type.construct(0.0));
ASSERT_FALSE(type.from_void(static_cast<void *>(nullptr)));
ASSERT_FALSE(type.from_void(static_cast<void *>(nullptr), true));
ASSERT_FALSE(type.from_void(static_cast<void *>(&type)));
ASSERT_FALSE(type.from_void(static_cast<void *>(&type), true));
ASSERT_FALSE(type.from_void(static_cast<const void *>(nullptr)));
ASSERT_FALSE(type.from_void(static_cast<const void *>(&type)));
ASSERT_FALSE(type.invoke("func"_hs, {}, args, 0u));
ASSERT_FALSE(type.invoke("func"_hs, {}, args, 1u));
ASSERT_FALSE(type.invoke("func"_hs, {}));
ASSERT_FALSE(type.invoke("func"_hs, {}, 'c'));
ASSERT_FALSE(type.set("data"_hs, {}, 0));
ASSERT_FALSE(type.get("data"_hs, {}));
ASSERT_EQ(type.traits<test::meta_traits>(), test::meta_traits::none);
ASSERT_EQ(static_cast<const char *>(type.custom()), nullptr);
}
TEST_F(MetaType, UserTraits) {
ASSERT_EQ(entt::resolve<bool>().traits<test::meta_traits>(), test::meta_traits::none);
ASSERT_EQ(entt::resolve<clazz>().traits<test::meta_traits>(), test::meta_traits::none);
ASSERT_EQ(entt::resolve<double>().traits<test::meta_traits>(), test::meta_traits::one);
ASSERT_EQ(entt::resolve<unsigned int>().traits<test::meta_traits>(), test::meta_traits::two);
ASSERT_EQ(entt::resolve<derived>().traits<test::meta_traits>(), test::meta_traits::one | test::meta_traits::three);
ASSERT_EQ(entt::resolve<property_type>().traits<test::meta_traits>(), test::meta_traits::two | test::meta_traits::three);
}
ENTT_DEBUG_TEST_F(MetaTypeDeathTest, UserTraits) {
using traits_type = entt::internal::meta_traits;
constexpr auto value = traits_type{static_cast<std::underlying_type_t<traits_type>>(traits_type::_user_defined_traits) + 1u};
ASSERT_DEATH(entt::meta_factory<clazz>{}.traits(value), "");
}
TEST_F(MetaType, Custom) {
ASSERT_EQ(*static_cast<const char *>(entt::resolve<clazz>().custom()), 'c');
ASSERT_EQ(static_cast<const char &>(entt::resolve<clazz>().custom()), 'c');
ASSERT_EQ(static_cast<const int *>(entt::resolve<clazz>().custom()), nullptr);
ASSERT_EQ(static_cast<const int *>(entt::resolve<base>().custom()), nullptr);
}
ENTT_DEBUG_TEST_F(MetaTypeDeathTest, Custom) {
ASSERT_DEATH([[maybe_unused]] const int value = entt::resolve<clazz>().custom(), "");
ASSERT_DEATH([[maybe_unused]] const char value = entt::resolve<base>().custom(), "");
}
TEST_F(MetaType, IdAndInfo) {
using namespace entt::literals;
auto type = entt::resolve<clazz>();
ASSERT_TRUE(type);
ASSERT_NE(type, entt::meta_type{});
ASSERT_EQ(type.id(), "class"_hs);
ASSERT_EQ(type.info(), entt::type_id<clazz>());
}
TEST_F(MetaType, SizeOf) {
ASSERT_EQ(entt::resolve<void>().size_of(), 0u);
ASSERT_EQ(entt::resolve<int>().size_of(), sizeof(int));
// NOLINTBEGIN(*-avoid-c-arrays)
ASSERT_EQ(entt::resolve<int[]>().size_of(), 0u);
ASSERT_EQ(entt::resolve<int[3]>().size_of(), sizeof(int[3]));
// NOLINTEND(*-avoid-c-arrays)
}
TEST_F(MetaType, Traits) {
ASSERT_TRUE(entt::resolve<bool>().is_arithmetic());
ASSERT_TRUE(entt::resolve<double>().is_arithmetic());
ASSERT_FALSE(entt::resolve<clazz>().is_arithmetic());
ASSERT_TRUE(entt::resolve<int>().is_integral());
ASSERT_FALSE(entt::resolve<double>().is_integral());
ASSERT_FALSE(entt::resolve<clazz>().is_integral());
ASSERT_TRUE(entt::resolve<long>().is_signed());
ASSERT_FALSE(entt::resolve<unsigned int>().is_signed());
ASSERT_FALSE(entt::resolve<clazz>().is_signed());
// NOLINTBEGIN(*-avoid-c-arrays)
ASSERT_TRUE(entt::resolve<int[5]>().is_array());
ASSERT_TRUE(entt::resolve<int[5][3]>().is_array());
// NOLINTEND(*-avoid-c-arrays)
ASSERT_FALSE(entt::resolve<int>().is_array());
ASSERT_TRUE(entt::resolve<property_type>().is_enum());
ASSERT_FALSE(entt::resolve<char>().is_enum());
ASSERT_TRUE(entt::resolve<derived>().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>().remove_pointer(), entt::resolve<derived>());
}
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, CanCast) {
auto type = entt::resolve<derived>();
ASSERT_FALSE(type.can_cast(entt::resolve<void>()));
ASSERT_TRUE(type.can_cast(entt::resolve<base>()));
ASSERT_TRUE(type.can_cast(entt::resolve<derived>()));
}
TEST_F(MetaType, CanConvert) {
auto instance = entt::resolve<clazz>();
auto other = entt::resolve<derived>();
auto arithmetic = entt::resolve<int>();
ASSERT_TRUE(instance.can_convert(entt::resolve<clazz>()));
ASSERT_TRUE(instance.can_convert(entt::resolve<int>()));
ASSERT_TRUE(other.can_convert(entt::resolve<derived>()));
ASSERT_TRUE(other.can_convert(entt::resolve<base>()));
ASSERT_FALSE(other.can_convert(entt::resolve<int>()));
ASSERT_TRUE(arithmetic.can_convert(entt::resolve<int>()));
ASSERT_FALSE(arithmetic.can_convert(entt::resolve<clazz>()));
ASSERT_TRUE(arithmetic.can_convert(entt::resolve<double>()));
ASSERT_TRUE(arithmetic.can_convert(entt::resolve<float>()));
}
TEST_F(MetaType, Base) {
auto type = entt::resolve<derived>();
ASSERT_NE(type.base().cbegin(), type.base().cend());
for(auto curr: type.base()) {
ASSERT_EQ(curr.first, entt::type_id<base>().hash());
ASSERT_EQ(curr.second, entt::resolve<base>());
}
}
TEST_F(MetaType, Ctor) {
derived instance;
base &as_base = instance;
auto type = entt::resolve<clazz>();
ASSERT_TRUE((type.construct(entt::forward_as_meta(instance), 3)));
ASSERT_TRUE((type.construct(entt::forward_as_meta(as_base), 3)));
// use the implicitly generated default constructor
auto any = type.construct();
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<clazz>());
}
TEST_F(MetaType, Data) {
using namespace entt::literals;
auto type = entt::resolve<clazz>();
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>();
clazz 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>();
clazz instance{};
ASSERT_TRUE(type.invoke("member"_hs, instance));
ASSERT_FALSE(type.invoke("rebmem"_hs, instance));
ASSERT_TRUE(type.invoke("func"_hs, {}));
ASSERT_FALSE(type.invoke("cnuf"_hs, {}));
}
TEST_F(MetaType, InvokeFromBase) {
using namespace entt::literals;
auto type = entt::resolve<concrete>();
concrete instance{};
ASSERT_TRUE(type.invoke("base_only"_hs, instance, 3));
ASSERT_FALSE(type.invoke("ylno_esab"_hs, {}, 'c'));
}
TEST_F(MetaType, OverloadedFunc) {
using namespace entt::literals;
const auto type = entt::resolve<overloaded_func>();
overloaded_func instance{};
entt::meta_any res{};
ASSERT_TRUE(type.func("f"_hs));
res = type.invoke("f"_hs, instance, base{}, 1, 2);
ASSERT_TRUE(res);
ASSERT_EQ(instance.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(instance.value, 3);
ASSERT_NE(res.try_cast<int>(), nullptr);
ASSERT_EQ(res.cast<int>(), 16);
res = type.invoke("f"_hs, instance, 2);
ASSERT_TRUE(res);
ASSERT_EQ(instance.value, 3);
ASSERT_NE(res.try_cast<int>(), nullptr);
ASSERT_EQ(res.cast<int>(), 12);
res = type.invoke("f"_hs, std::as_const(instance), 2);
ASSERT_TRUE(res);
ASSERT_EQ(instance.value, 3);
ASSERT_NE(res.try_cast<int>(), nullptr);
ASSERT_EQ(res.cast<int>(), 6);
res = type.invoke("f"_hs, instance, 0, 1.f);
ASSERT_TRUE(res);
ASSERT_EQ(instance.value, 0);
ASSERT_NE(res.try_cast<float>(), nullptr);
ASSERT_EQ(res.cast<float>(), 2.f);
res = type.invoke("f"_hs, instance, 4, 8.f);
ASSERT_TRUE(res);
ASSERT_EQ(instance.value, 4);
ASSERT_NE(res.try_cast<float>(), nullptr);
ASSERT_EQ(res.cast<float>(), 16.f);
// it fails as an ambiguous call
ASSERT_FALSE(type.invoke("f"_hs, instance, 4, 8.));
}
TEST_F(MetaType, OverloadedFuncOrder) {
using namespace entt::literals;
const auto type = entt::resolve<overloaded_func>();
auto func = type.func("f"_hs);
ASSERT_TRUE(func);
ASSERT_EQ(func.arity(), 3u);
ASSERT_FALSE(func.is_const());
ASSERT_EQ(func.ret(), entt::resolve<int>());
func = func.next();
ASSERT_TRUE(func);
ASSERT_EQ(func.arity(), 2u);
ASSERT_FALSE(func.is_const());
ASSERT_EQ(func.ret(), entt::resolve<int>());
func = func.next();
ASSERT_TRUE(func);
ASSERT_EQ(func.arity(), 1u);
ASSERT_FALSE(func.is_const());
ASSERT_EQ(func.ret(), entt::resolve<int>());
func = func.next();
ASSERT_TRUE(func);
ASSERT_EQ(func.arity(), 1u);
ASSERT_TRUE(func.is_const());
ASSERT_EQ(func.ret(), entt::resolve<int>());
func = func.next();
ASSERT_TRUE(func);
ASSERT_EQ(func.arity(), 2u);
ASSERT_FALSE(func.is_const());
ASSERT_EQ(func.ret(), entt::resolve<float>());
}
TEST_F(MetaType, Construct) {
auto any = entt::resolve<clazz>().construct(base{}, 2);
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz>().value, 2);
}
TEST_F(MetaType, ConstructNoArgs) {
// this should work, no other tests required
auto any = entt::resolve<clazz>().construct();
ASSERT_TRUE(any);
}
TEST_F(MetaType, ConstructMetaAnyArgs) {
auto any = entt::resolve<clazz>().construct(entt::meta_any{base{}}, entt::meta_any{3});
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz>().value, 3);
}
TEST_F(MetaType, ConstructInvalidArgs) {
ASSERT_FALSE(entt::resolve<clazz>().construct('c', base{}));
}
TEST_F(MetaType, LessArgs) {
ASSERT_FALSE(entt::resolve<clazz>().construct(base{}));
}
TEST_F(MetaType, ConstructCastAndConvert) {
auto any = entt::resolve<clazz>().construct(derived{}, clazz{derived{}, 3});
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz>().value, 3);
}
TEST_F(MetaType, ConstructArithmeticConversion) {
auto any = entt::resolve<clazz>().construct(derived{}, true);
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz>().value, 1);
}
TEST_F(MetaType, FromVoid) {
ASSERT_FALSE(entt::resolve<double>().from_void(static_cast<double *>(nullptr)));
ASSERT_FALSE(entt::resolve<double>().from_void(static_cast<const double *>(nullptr)));
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 type = entt::resolve<double>();
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, FromVoidOwnership) {
bool check = false;
auto type = entt::resolve<from_void_callback>();
void *instance = std::make_unique<from_void_callback>(check).release();
auto any = type.from_void(instance);
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
auto other = type.from_void(instance, true);
ASSERT_TRUE(any);
ASSERT_TRUE(other);
ASSERT_FALSE(check);
any.reset();
ASSERT_FALSE(check);
other.reset();
ASSERT_TRUE(check);
}
TEST_F(MetaType, Reset) {
using namespace entt::literals;
ASSERT_TRUE(entt::resolve("class"_hs));
ASSERT_EQ(entt::resolve<clazz>().id(), "class"_hs);
ASSERT_TRUE(entt::resolve<clazz>().data("value"_hs));
ASSERT_TRUE((entt::resolve<clazz>().construct(derived{}, clazz{})));
// implicitly generated default constructor
ASSERT_TRUE(entt::resolve<clazz>().construct());
entt::meta_reset("class"_hs);
ASSERT_FALSE(entt::resolve("class"_hs));
ASSERT_NE(entt::resolve<clazz>().id(), "class"_hs);
ASSERT_FALSE(entt::resolve<clazz>().data("value"_hs));
ASSERT_FALSE((entt::resolve<clazz>().construct(derived{}, clazz{})));
// implicitly generated default constructor is not cleared
ASSERT_TRUE(entt::resolve<clazz>().construct());
entt::meta_factory<clazz>{}.type("class"_hs);
ASSERT_TRUE(entt::resolve("class"_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("class"_hs));
ASSERT_TRUE(entt::resolve("overloaded_func"_hs));
ASSERT_TRUE(entt::resolve("double"_hs));
entt::meta_reset();
ASSERT_FALSE(entt::resolve("class"_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>();
concrete instance;
ASSERT_EQ(type.info(), entt::type_id<abstract>());
ASSERT_EQ(instance.base::value, 'c');
ASSERT_EQ(instance.value, 3);
type.func("func"_hs).invoke(instance, 2);
ASSERT_EQ(instance.base::value, 'c');
ASSERT_EQ(instance.value, 2);
}
TEST_F(MetaType, EnumAndNamedConstants) {
using namespace entt::literals;
auto type = entt::resolve<property_type>();
ASSERT_TRUE(type.data("value"_hs));
ASSERT_TRUE(type.data("other"_hs));
ASSERT_EQ(type.data("value"_hs).type(), type);
ASSERT_EQ(type.data("other"_hs).type(), type);
ASSERT_EQ(type.data("value"_hs).get({}).cast<property_type>(), property_type::value);
ASSERT_EQ(type.data("other"_hs).get({}).cast<property_type>(), property_type::other);
ASSERT_FALSE(type.data("value"_hs).set({}, property_type::other));
ASSERT_FALSE(type.data("other"_hs).set({}, property_type::value));
ASSERT_EQ(type.data("value"_hs).get({}).cast<property_type>(), property_type::value);
ASSERT_EQ(type.data("other"_hs).get({}).cast<property_type>(), property_type::other);
}
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({}, 128u));
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>(), 128u);
}
TEST_F(MetaType, Variables) {
using namespace entt::literals;
auto p_data = entt::resolve<property_type>().data("var"_hs);
auto d_data = entt::resolve("double"_hs).data("var"_hs);
property_type prop{property_type::value};
double value = 3.;
p_data.set(prop, property_type::other);
d_data.set(value, 3.);
ASSERT_EQ(p_data.get(prop).cast<property_type>(), property_type::other);
ASSERT_EQ(d_data.get(value).cast<double>(), 3.);
ASSERT_EQ(prop, property_type::other);
ASSERT_EQ(value, 3.);
}
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>();
entt::meta_reset<derived>();
entt::meta_reset<abstract>();
entt::meta_reset<concrete>();
entt::meta_reset<overloaded_func>();
entt::meta_reset<property_type>();
entt::meta_reset<clazz>();
ASSERT_FALSE(entt::resolve("double"_hs));
ASSERT_FALSE(entt::resolve("base"_hs));
ASSERT_FALSE(entt::resolve("derived"_hs));
ASSERT_FALSE(entt::resolve("class"_hs));
ASSERT_TRUE(entt::internal::meta_context::from(entt::locator<entt::meta_ctx>::value_or()).value.empty());
// implicitly generated default constructor is not cleared
ASSERT_TRUE(entt::resolve<clazz>().construct());
ASSERT_FALSE(entt::resolve<clazz>().data("value"_hs));
ASSERT_FALSE(entt::resolve<clazz>().func("member"_hs));
entt::meta_factory<double>{}.type("double"_hs);
entt::meta_any any{3.};
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_factory<base>{}
.traits(test::meta_traits::one)
.custom<int>(3)
// this should not overwrite traits and custom data
.type("base"_hs);
// this should not overwrite traits and custom data
[[maybe_unused]] const entt::meta_factory<base> factory{};
ASSERT_EQ(entt::resolve<base>().traits<test::meta_traits>(), test::meta_traits::one);
ASSERT_NE(static_cast<const int *>(entt::resolve("base"_hs).custom()), nullptr);
}
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_factory<double>{}
.type("real"_hs)
.traits(test::meta_traits::one)
.custom<int>(3);
// this should not overwrite traits and custom data
entt::meta_factory<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));
ASSERT_EQ(entt::resolve<double>().traits<test::meta_traits>(), test::meta_traits::one);
ASSERT_NE(static_cast<const int *>(entt::resolve<double>().custom()), nullptr);
}
TEST_F(MetaType, NameCollision) {
using namespace entt::literals;
ASSERT_NO_THROW(entt::meta_factory<clazz>{}.type("class"_hs));
ASSERT_TRUE(entt::resolve("class"_hs));
ASSERT_NO_THROW(entt::meta_factory<clazz>{}.type("quux"_hs));
ASSERT_FALSE(entt::resolve("class"_hs));
ASSERT_TRUE(entt::resolve("quux"_hs));
}
ENTT_DEBUG_TEST_F(MetaTypeDeathTest, NameCollision) {
using namespace entt::literals;
ASSERT_DEATH(entt::meta_factory<clazz>{}.type("abstract"_hs), "");
}

View File

@@ -0,0 +1,272 @@
#include <array>
#include <iterator>
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/type_traits.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/policy.hpp>
#include <entt/meta/resolve.hpp>
#include <entt/meta/utility.hpp>
#include "../../common/config.h"
#include "../../common/empty.h"
struct clazz {
void setter(int iv) {
member = iv;
}
[[nodiscard]] int getter() const {
return member;
}
static void static_setter(clazz &instance, int iv) {
instance.member = iv;
}
[[nodiscard]] static int static_getter(const clazz &instance) {
return instance.member;
}
static void reset_value() {
value = 0;
}
[[nodiscard]] static int get_value() {
return value;
}
[[nodiscard]] static clazz factory(int iv) {
clazz instance{};
instance.member = iv;
return instance;
}
int member{};
const int cmember{}; // NOLINT
inline static int value{}; // NOLINT
inline static const int cvalue{}; // NOLINT
inline static int arr[3u]{}; // NOLINT
};
struct MetaUtility: ::testing::Test {
void SetUp() override {
clazz::value = 0;
}
};
using MetaUtilityDeathTest = MetaUtility;
TEST_F(MetaUtility, MetaDispatch) {
int value = 2;
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>(), 2);
ASSERT_EQ(as_ref.cast<int>(), 2);
ASSERT_EQ(as_cref.cast<int>(), 2);
}
TEST_F(MetaUtility, MetaDispatchMetaAny) {
entt::meta_any any{2};
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>(), 2);
ASSERT_EQ(from_const_any.cast<int>(), 2);
}
TEST_F(MetaUtility, MetaDispatchMetaAnyAsRef) {
entt::meta_any any{2};
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>(), 2);
ASSERT_EQ(from_const_any.cast<int>(), 2);
}
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), 4)));
ASSERT_FALSE((entt::meta_setter<clazz, &clazz::static_setter>(invalid, 4)));
ASSERT_TRUE((entt::meta_setter<clazz, &clazz::static_setter>(instance, 4)));
ASSERT_EQ(instance.member, 4);
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, 8)));
ASSERT_TRUE((entt::meta_setter<clazz, &clazz::member>(instance, 8)));
ASSERT_EQ(instance.member, 8);
ASSERT_FALSE((entt::meta_setter<clazz, &clazz::cmember>(instance, 8)));
ASSERT_FALSE((entt::meta_setter<clazz, &clazz::cmember>(invalid, 8)));
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>(entt::meta_any{}.context(), instance)).cast<int>(), 0);
ASSERT_FALSE((entt::meta_getter<clazz, &clazz::getter>(invalid)));
ASSERT_EQ((entt::meta_getter<clazz, &clazz::getter>(entt::meta_any{}.context(), instance)).cast<int>(), 0);
ASSERT_FALSE((entt::meta_getter<clazz, &clazz::member>(invalid)));
ASSERT_EQ((entt::meta_getter<clazz, &clazz::member>(entt::meta_any{}.context(), 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>(entt::meta_any{}.context(), 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>(entt::meta_any{}.context(), instance)));
ASSERT_EQ((entt::meta_getter<clazz, &clazz::value>(invalid)).cast<int>(), 0);
ASSERT_EQ((entt::meta_getter<clazz, &clazz::value>(entt::meta_any{}.context(), instance)).cast<int>(), 0);
ASSERT_EQ((entt::meta_getter<clazz, &clazz::cvalue>(invalid)).cast<int>(), 0);
ASSERT_EQ((entt::meta_getter<clazz, &clazz::cvalue>(entt::meta_any{}.context(), instance)).cast<int>(), 0);
ASSERT_EQ((entt::meta_getter<clazz, 1>(invalid)).cast<int>(), 1);
ASSERT_EQ((entt::meta_getter<clazz, 1>(entt::meta_any{}.context(), instance)).cast<int>(), 1);
}
TEST_F(MetaUtility, MetaInvokeWithCandidate) {
std::array args{entt::meta_any{clazz{}}, entt::meta_any{4}};
clazz::value = 3;
ASSERT_FALSE((entt::meta_invoke<clazz>({}, &clazz::setter, std::next(args.data()))));
ASSERT_FALSE((entt::meta_invoke<clazz>(entt::meta_any{}.context(), {}, &clazz::getter, nullptr)));
ASSERT_TRUE((entt::meta_invoke<clazz>(args[0u], &clazz::setter, std::next(args.data()))));
ASSERT_FALSE((entt::meta_invoke<clazz>(entt::meta_any{}.context(), args[0u], &clazz::setter, args.data())));
ASSERT_EQ((entt::meta_invoke<clazz>(args[0u], &clazz::getter, nullptr)).cast<int>(), 4);
ASSERT_FALSE((entt::meta_invoke<clazz>(entt::meta_any{}.context(), args[1u], &clazz::getter, nullptr)));
ASSERT_EQ((entt::meta_invoke<clazz>({}, &clazz::get_value, nullptr)).cast<int>(), 3);
ASSERT_TRUE((entt::meta_invoke<clazz>(entt::meta_any{}.context(), {}, &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<test::empty>({}, setter, std::next(args.data())));
ASSERT_EQ(entt::meta_invoke<test::empty>(entt::meta_any{}.context(), {}, getter, std::next(args.data())).cast<int>(), 6);
}
TEST_F(MetaUtility, MetaInvoke) {
std::array args{entt::meta_any{clazz{}}, entt::meta_any{4}};
clazz::value = 3;
ASSERT_FALSE((entt::meta_invoke<clazz, &clazz::setter>({}, std::next(args.data()))));
ASSERT_FALSE((entt::meta_invoke<clazz, &clazz::getter>(entt::meta_any{}.context(), {}, nullptr)));
ASSERT_TRUE((entt::meta_invoke<clazz, &clazz::setter>(args[0u], std::next(args.data()))));
ASSERT_FALSE((entt::meta_invoke<clazz, &clazz::setter>(entt::meta_any{}.context(), args[0u], args.data())));
ASSERT_EQ((entt::meta_invoke<clazz, &clazz::getter>(args[0u], nullptr)).cast<int>(), 4);
ASSERT_FALSE((entt::meta_invoke<clazz, &clazz::getter>(entt::meta_any{}.context(), args[1u], nullptr)));
ASSERT_EQ((entt::meta_invoke<clazz, &clazz::get_value>({}, nullptr)).cast<int>(), 3);
ASSERT_TRUE((entt::meta_invoke<clazz, &clazz::reset_value>(entt::meta_any{}.context(), {}, nullptr)));
ASSERT_EQ(args[0u].cast<clazz &>().value, 0);
}
TEST_F(MetaUtility, MetaConstructArgsOnly) {
std::array args{entt::meta_any{clazz{}}, entt::meta_any{4}};
const auto any = entt::meta_construct<clazz, int>(std::next(args.data()));
ASSERT_TRUE(any);
ASSERT_FALSE((entt::meta_construct<clazz, int>(args.data())));
ASSERT_EQ(any.cast<const clazz &>().member, 4);
}
TEST_F(MetaUtility, MetaConstructWithCandidate) {
std::array args{entt::meta_any{clazz{}}, entt::meta_any{4}};
const auto any = entt::meta_construct<clazz>(&clazz::factory, std::next(args.data()));
ASSERT_TRUE(any);
ASSERT_FALSE((entt::meta_construct<clazz>(&clazz::factory, args.data())));
ASSERT_EQ(any.cast<const clazz &>().member, 4);
ASSERT_EQ(args[0u].cast<const clazz &>().member, 0);
ASSERT_TRUE((entt::meta_construct<clazz>(&clazz::static_setter, args.data())));
ASSERT_EQ(args[0u].cast<const clazz &>().member, 4);
const auto setter = [](int &value) { value = 3; };
const auto builder = [](int value) { return value * 2; };
ASSERT_TRUE(entt::meta_construct<test::empty>(setter, std::next(args.data())));
ASSERT_EQ(entt::meta_construct<test::empty>(builder, std::next(args.data())).cast<int>(), 6);
}
TEST_F(MetaUtility, MetaConstruct) {
std::array args{entt::meta_any{clazz{}}, entt::meta_any{4}};
const auto any = entt::meta_construct<clazz, &clazz::factory>(std::next(args.data()));
ASSERT_TRUE(any);
ASSERT_FALSE((entt::meta_construct<clazz, &clazz::factory>(args.data())));
ASSERT_EQ(any.cast<const clazz &>().member, 4);
ASSERT_EQ(args[0u].cast<const clazz &>().member, 0);
ASSERT_TRUE((entt::meta_construct<clazz, &clazz::static_setter>(args.data())));
ASSERT_EQ(args[0u].cast<const clazz &>().member, 4);
}

View File

@@ -0,0 +1,498 @@
#include <array>
#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"
#include "../../common/linter.hpp"
template<typename Base>
struct common_type: Base {
void incr() {
constexpr auto member_index = 0u;
entt::poly_call<member_index>(*this);
}
void set(int iv) {
constexpr auto member_index = 1u;
entt::poly_call<member_index>(*this, iv);
}
[[nodiscard]] int get() const {
constexpr auto member_index = 2u;
return static_cast<int>(entt::poly_call<member_index>(*this));
}
void decr() {
constexpr auto member_index = 3u;
entt::poly_call<member_index>(*this);
}
[[nodiscard]] int mul(int iv) const {
constexpr auto member_index = 4u;
return static_cast<int>(entt::poly_call<member_index>(*this, iv));
}
[[nodiscard]] int rand() const {
constexpr auto member_index = 5u;
return static_cast<int>(entt::poly_call<member_index>(*this));
}
};
template<typename Type>
struct common_members {
static void decr(Type &self) {
self.set(self.get() - 1);
}
[[nodiscard]] static double mul(const Type &self, double dv) {
return dv * self.get();
}
};
namespace {
template<typename... Type>
entt::type_list<Type...> as_type_list(const entt::type_list<Type...> &);
[[nodiscard]] int absolutely_random() {
return 4;
}
[[nodiscard]] int three_is_a_magic_number() {
return 3;
}
} // namespace
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 {
[[nodiscard]] 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
[[nodiscard]] int get() {
return entt::poly_call<0>(*this);
}
};
template<typename Type>
using impl = entt::value_list<&Type::get>;
};
struct DeducedDerived
: entt::type_list<> {
template<typename Base>
struct type: Deduced::type<Base> {
static constexpr auto base = Deduced::impl<Deduced::type<entt::poly_inspector>>::size;
int three_is_a_magic_number() {
return entt::poly_call<base + 0>(*this);
}
};
template<typename Type>
using impl = entt::value_list_cat_t<typename Deduced::impl<Type>, entt::value_list<&three_is_a_magic_number>>;
};
struct DefinedDerived
: entt::type_list_cat_t<
decltype(as_type_list(std::declval<Defined>())),
entt::type_list<int()>> {
template<typename Base>
struct type: Defined::type<Base> {
static constexpr auto base = Defined::impl<Defined::type<entt::poly_inspector>>::size;
int three_is_a_magic_number() {
return entt::poly_call<base + 0>(*this);
}
};
template<typename Type>
using impl = entt::value_list_cat_t<typename Defined::impl<Type>, entt::value_list<&three_is_a_magic_number>>;
};
struct impl {
impl() = default;
impl(int iv)
: value{iv} {}
void incr() {
++value;
}
void set(int iv) {
value = iv;
}
[[nodiscard]] 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, );
template<typename Type>
struct PolyDerived: testing::Test {
using type = entt::basic_poly<Type>;
};
using PolyDerivedTypes = ::testing::Types<DeducedDerived, DefinedDerived>;
TYPED_TEST_SUITE(PolyDerived, PolyDerivedTypes, );
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(), absolutely_random());
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);
test::is_initialized(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(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);
}
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);
}
ENTT_DEBUG_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) {
constexpr auto alignment = alignof(over_aligned);
using poly_type = typename TestFixture::template type<alignment, alignment>;
std::array<poly_type, 2u> sbo = {over_aligned{}, over_aligned{}};
const auto *data = sbo[0].data();
// NOLINTBEGIN(*-reinterpret-cast)
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(sbo[0u].data()) % alignment) == 0u);
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(sbo[1u].data()) % alignment) == 0u);
// NOLINTEND(*-reinterpret-cast)
std::swap(sbo[0], sbo[1]);
// NOLINTBEGIN(*-reinterpret-cast)
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(sbo[0u].data()) % alignment) == 0u);
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(sbo[1u].data()) % alignment) == 0u);
// NOLINTEND(*-reinterpret-cast)
ASSERT_NE(data, sbo[1].data());
}
TYPED_TEST(Poly, NoSboAlignment) {
constexpr auto alignment = alignof(over_aligned);
using poly_type = typename TestFixture::template type<alignment>;
std::array<poly_type, 2u> nosbo = {over_aligned{}, over_aligned{}};
const auto *data = nosbo[0].data();
// NOLINTBEGIN(*-reinterpret-cast)
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(nosbo[0u].data()) % alignment) == 0u);
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(nosbo[1u].data()) % alignment) == 0u);
// NOLINTEND(*-reinterpret-cast)
std::swap(nosbo[0], nosbo[1]);
// NOLINTBEGIN(*-reinterpret-cast)
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(nosbo[0u].data()) % alignment) == 0u);
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(nosbo[1u].data()) % alignment) == 0u);
// NOLINTEND(*-reinterpret-cast)
ASSERT_EQ(data, nosbo[1].data());
}
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(PolyDerived, InheritanceSupport) {
using poly_type = typename TestFixture::type;
poly_type poly{impl{}};
ASSERT_EQ(poly->three_is_a_magic_number(), three_is_a_magic_number());
}

View File

@@ -0,0 +1,261 @@
#include <cstdint>
#include <gtest/gtest.h>
#include <entt/process/process.hpp>
#include "../../common/empty.h"
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;
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 != nullptr) {
(*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<test::empty> 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<test::empty> 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<test::empty> 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) = 2;
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, 2);
}

View File

@@ -0,0 +1,182 @@
#include <functional>
#include <memory>
#include <utility>
#include <gtest/gtest.h>
#include <entt/process/process.hpp>
#include <entt/process/scheduler.hpp>
struct foo_process: entt::process<foo_process, entt::scheduler::delta_type> {
foo_process(std::function<void()> upd, std::function<void()> abort)
: on_update{std::move(upd)}, on_aborted{std::move(abort)} {}
void update(delta_type, void *) const {
on_update();
}
void aborted() const {
on_aborted();
}
std::function<void()> on_update;
std::function<void()> on_aborted;
};
struct succeeded_process: entt::process<succeeded_process, entt::scheduler::delta_type> {
void update(delta_type, void *data) {
++static_cast<std::pair<int, int> *>(data)->first;
succeed();
}
};
struct failed_process: entt::process<failed_process, entt::scheduler::delta_type> {
void update(delta_type, void *data) {
++static_cast<std::pair<int, int> *>(data)->second;
fail();
}
};
TEST(Scheduler, Functionalities) {
entt::scheduler scheduler{};
entt::scheduler other{std::move(scheduler)};
scheduler = std::move(other);
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(Scheduler, Swap) {
entt::scheduler scheduler{};
entt::scheduler other{};
int counter{};
scheduler.attach([&counter](auto &&...) { ++counter; });
ASSERT_EQ(scheduler.size(), 1u);
ASSERT_EQ(other.size(), 0u);
ASSERT_EQ(counter, 0);
scheduler.update({});
ASSERT_EQ(counter, 1);
scheduler.swap(other);
scheduler.update({});
ASSERT_EQ(scheduler.size(), 0u);
ASSERT_EQ(other.size(), 1u);
ASSERT_EQ(counter, 1);
other.update({});
ASSERT_EQ(counter, 2);
}
TEST(Scheduler, Then) {
entt::scheduler scheduler{};
std::pair<int, int> counter{};
scheduler
// failing process with successor
.attach<succeeded_process>()
.then<succeeded_process>()
.then<failed_process>()
.then<succeeded_process>()
// failing process without successor
.attach<succeeded_process>()
.then<succeeded_process>()
.then<failed_process>()
// non-failing process
.attach<succeeded_process>()
.then<succeeded_process>();
while(!scheduler.empty()) {
scheduler.update(0, &counter);
}
ASSERT_EQ(counter.first, 6u);
ASSERT_EQ(counter.second, 2u);
}
TEST(Scheduler, Functor) {
entt::scheduler 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(Scheduler, SpawningProcess) {
entt::scheduler scheduler{};
std::pair<int, int> counter{};
scheduler.attach([&scheduler](auto, void *, auto resolve, auto) {
scheduler.attach<succeeded_process>().then<failed_process>();
resolve();
});
while(!scheduler.empty()) {
scheduler.update(0, &counter);
}
ASSERT_EQ(counter.first, 1u);
ASSERT_EQ(counter.second, 1u);
}
TEST(Scheduler, CustomAllocator) {
const std::allocator<void> allocator{};
entt::scheduler scheduler{allocator};
ASSERT_EQ(scheduler.get_allocator(), allocator);
ASSERT_FALSE(scheduler.get_allocator() != allocator);
scheduler.attach([](auto &&...) {});
const decltype(scheduler) other{std::move(scheduler), allocator};
ASSERT_EQ(other.size(), 1u);
}

View File

@@ -0,0 +1,187 @@
#include <memory>
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/type_info.hpp>
#include <entt/resource/resource.hpp>
#include "../../common/linter.hpp"
struct base {
virtual ~base() = default;
[[nodiscard]] virtual const entt::type_info &type() const noexcept {
return entt::type_id<base>();
}
};
struct derived: base {
[[nodiscard]] 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) {
const 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);
copy.reset(std::make_shared<derived>());
ASSERT_TRUE(copy);
ASSERT_TRUE(move);
ASSERT_NE(copy, move);
move.reset();
ASSERT_TRUE(copy);
ASSERT_FALSE(move);
ASSERT_NE(copy, move);
}
TEST(Resource, Swap) {
entt::resource<int> resource{};
entt::resource<int> other{};
ASSERT_FALSE(resource);
ASSERT_FALSE(other);
resource.swap(other);
ASSERT_FALSE(resource);
ASSERT_FALSE(other);
resource.reset(std::make_shared<int>(1));
ASSERT_TRUE(resource);
ASSERT_EQ(*resource, 1);
ASSERT_FALSE(other);
resource.swap(other);
ASSERT_FALSE(resource);
ASSERT_TRUE(other);
ASSERT_EQ(*other, 1);
}
TEST(Resource, DerivedToBase) {
const 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};
testing::StaticAssertTypeEq<decltype(*resource), derived &>();
testing::StaticAssertTypeEq<decltype(*entt::resource<const derived>{other}), const derived &>();
testing::StaticAssertTypeEq<decltype(*std::as_const(resource)), derived &>();
entt::resource<const derived> copy{resource};
entt::resource<const derived> move{std::move(other)};
test::is_initialized(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(), 3);
ASSERT_TRUE(move);
ASSERT_EQ(move, resource);
ASSERT_NE(move, entt::resource<derived>{});
ASSERT_EQ(move.handle().use_count(), 3);
copy = resource;
move = std::move(resource);
test::is_initialized(resource);
ASSERT_FALSE(resource);
ASSERT_FALSE(other);
ASSERT_TRUE(copy);
ASSERT_TRUE(move);
ASSERT_EQ(copy.handle().use_count(), 2);
}
TEST(Resource, DynamicResourceHandleCast) {
const entt::resource<derived> resource{std::make_shared<derived>()};
entt::resource<const base> other = resource;
ASSERT_TRUE(other);
ASSERT_EQ(resource.handle().use_count(), 2);
ASSERT_EQ(resource, other);
entt::resource<const derived> cast = dynamic_resource_cast<const derived>(other);
ASSERT_TRUE(cast);
ASSERT_EQ(resource.handle().use_count(), 3);
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(), 1);
}
TEST(Resource, Comparison) {
const entt::resource<derived> resource{std::make_shared<derived>()};
const 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/container/dense_map.hpp>
#include <entt/core/hashed_string.hpp>
#include <entt/core/iterator.hpp>
#include <entt/resource/cache.hpp>
#include <entt/resource/loader.hpp>
#include <entt/resource/resource.hpp>
#include "../../common/empty.h"
#include "../../common/linter.hpp"
#include "../../common/throwing_allocator.hpp"
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()(test::other_empty, Func &&func) const {
return std::forward<Func>(func)();
}
template<typename... Args>
result_type operator()(test::empty) const {
return {};
}
};
TEST(ResourceCache, Functionalities) {
using namespace entt::literals;
entt::resource_cache<int> cache;
ASSERT_NO_THROW([[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, 2);
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, 2u);
entt::resource_cache<int> temp{cache, cache.get_allocator()};
const 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, 3u);
entt::resource_cache<std::size_t> other{cache};
ASSERT_TRUE(cache.contains("resource"_hs));
ASSERT_TRUE(other.contains("resource"_hs));
cache.load("foo"_hs, 2u);
cache.load("bar"_hs, 1u);
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], 3u);
ASSERT_EQ(other["foo"_hs], 2u);
ASSERT_EQ(other["bar"_hs], 1u);
}
TEST(ResourceCache, Move) {
using namespace entt::literals;
entt::resource_cache<std::size_t> cache;
cache.load("resource"_hs, 3u);
entt::resource_cache<std::size_t> other{std::move(cache)};
test::is_initialized(cache);
ASSERT_TRUE(cache.empty());
ASSERT_TRUE(other.contains("resource"_hs));
cache = other;
cache.load("foo"_hs, 2u);
cache.load("bar"_hs, 1u);
other.load("quux"_hs, 0u);
other = std::move(cache);
test::is_initialized(cache);
ASSERT_TRUE(cache.empty());
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], 3u);
ASSERT_EQ(other["foo"_hs], 2u);
ASSERT_EQ(other["bar"_hs], 1u);
}
TEST(ResourceCache, Iterator) {
using namespace entt::literals;
using iterator = typename entt::resource_cache<int>::iterator;
testing::StaticAssertTypeEq<iterator::value_type, std::pair<entt::id_type, entt::resource<int>>>();
testing::StaticAssertTypeEq<iterator::pointer, entt::input_iterator_pointer<std::pair<entt::id_type, entt::resource<int>>>>();
testing::StaticAssertTypeEq<iterator::reference, std::pair<entt::id_type, entt::resource<int>>>();
entt::resource_cache<int> cache;
cache.load("resource"_hs, 2);
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;
testing::StaticAssertTypeEq<iterator::value_type, std::pair<entt::id_type, entt::resource<const int>>>();
testing::StaticAssertTypeEq<iterator::pointer, entt::input_iterator_pointer<std::pair<entt::id_type, entt::resource<const int>>>>();
testing::StaticAssertTypeEq<iterator::reference, std::pair<entt::id_type, entt::resource<const int>>>();
entt::resource_cache<int> cache;
cache.load("resource"_hs, 2);
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, 2);
const typename entt::resource_cache<int>::iterator it = cache.begin();
typename entt::resource_cache<int>::const_iterator cit = it;
testing::StaticAssertTypeEq<decltype(*it), std::pair<entt::id_type, entt::resource<int>>>();
testing::StaticAssertTypeEq<decltype(*cit), std::pair<entt::id_type, entt::resource<const int>>>();
ASSERT_EQ(it->first, "resource"_hs);
ASSERT_EQ((*it).second, 2);
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) {
constexpr std::size_t resource_count = 5u;
entt::resource_cache<std::size_t> cache;
for(std::size_t next{}; next < resource_count; ++next) {
cache.load(static_cast<entt::id_type>(next), next);
}
ASSERT_EQ(cache.size(), resource_count);
for(std::size_t next{}; next < resource_count; ++next) {
ASSERT_TRUE(cache.contains(static_cast<entt::id_type>(next)));
}
auto it = cache.erase(++cache.begin());
it = cache.erase(it, it + 1);
ASSERT_EQ((--cache.end())->first, 2u);
ASSERT_EQ(cache.erase(2u), 1u);
ASSERT_EQ(cache.erase(2u), 0u);
ASSERT_EQ(cache.size(), 2u);
ASSERT_EQ(it, ++cache.begin());
ASSERT_EQ(cache.begin()->first, 0u);
ASSERT_EQ((--cache.end())->first, 3u);
for(std::size_t next{}; next < resource_count; ++next) {
if(next == 1u || next == 2u || next == 4u) {
ASSERT_FALSE(cache.contains(static_cast<entt::id_type>(next)));
} else {
ASSERT_TRUE(cache.contains(static_cast<entt::id_type>(next)));
}
}
cache.erase(cache.begin(), cache.end());
for(std::size_t next{}; next < resource_count; ++next) {
ASSERT_FALSE(cache.contains(static_cast<entt::id_type>(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, 1);
ASSERT_TRUE(cache.contains("resource"_hs));
ASSERT_EQ(std::as_const(cache)["resource"_hs], 1);
ASSERT_EQ(cache["resource"_hs], 1);
}
TEST(ResourceCache, LoaderDispatching) {
using namespace entt::literals;
entt::resource_cache<int, loader<int>> cache;
cache.force_load("resource"_hs, 1);
ASSERT_TRUE(cache.contains("resource"_hs));
ASSERT_EQ(cache["resource"_hs], 1);
cache.force_load("resource"_hs, test::other_empty{}, []() { return std::make_shared<int>(2); });
ASSERT_TRUE(cache.contains("resource"_hs));
ASSERT_EQ(cache["resource"_hs], 2);
}
TEST(ResourceCache, BrokenLoader) {
using namespace entt::literals;
entt::resource_cache<int, loader<int>> cache;
cache.load("resource"_hs, test::empty{});
ASSERT_TRUE(cache.contains("resource"_hs));
ASSERT_FALSE(cache["resource"_hs]);
cache.force_load("resource"_hs, 2);
ASSERT_TRUE(cache.contains("resource"_hs));
ASSERT_TRUE(cache["resource"_hs]);
}
TEST(ResourceCache, ThrowingAllocator) {
using namespace entt::literals;
entt::resource_cache<std::size_t, entt::resource_loader<std::size_t>, test::throwing_allocator<std::size_t>> cache{};
cache.get_allocator().throw_counter<entt::internal::dense_map_node<entt::id_type, std::shared_ptr<std::size_t>>>(0u);
ASSERT_THROW(cache.load("resource"_hs), test::throwing_allocator_exception);
ASSERT_FALSE(cache.contains("resource"_hs));
cache.get_allocator().throw_counter<entt::internal::dense_map_node<entt::id_type, std::shared_ptr<std::size_t>>>(0u);
ASSERT_THROW(cache.force_load("resource"_hs), test::throwing_allocator_exception);
ASSERT_FALSE(cache.contains("resource"_hs));
}

View File

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

View File

@@ -0,0 +1,466 @@
#include <memory>
#include <utility>
#include <gtest/gtest.h>
#include <entt/signal/delegate.hpp>
#include "../../common/config.h"
int power_of_two(const int &iv) {
return iv * iv;
}
int sum_with_ref(const int &iv, int jv) {
return iv + jv;
}
int sum_with_ptr(const int *iv, int jv) {
return (*iv) + jv;
}
int non_const_reference(int &iv) {
return iv *= iv;
}
int move_only_type(std::unique_ptr<int> ptr) {
return *ptr;
}
struct delegate_functor {
int operator()(int iv) {
return iv + iv;
}
[[nodiscard]] int mul(int iv) const {
return iv * data_member;
}
static const int static_value = 3;
// NOLINTNEXTLINE(*-avoid-const-or-ref-data-members)
const int data_member = 4;
};
struct const_nonconst_noexcept {
void f() {
++cnt;
}
void g() noexcept {
++cnt;
}
void h() const {
++cnt;
}
void i() const noexcept {
++cnt;
}
int u{};
// NOLINTNEXTLINE(*-avoid-const-or-ref-data-members)
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<&power_of_two>();
mf_del.connect<&delegate_functor::operator()>(functor);
lf_del.connect([](const void *ptr, int value) { return static_cast<const delegate_functor *>(ptr)->mul(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), 12);
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(4), "");
ASSERT_DEATH(std::as_const(del)(4), "");
}
TEST(Delegate, DataMembers) {
entt::delegate<double()> delegate;
delegate_functor functor;
delegate.connect<&delegate_functor::data_member>(functor);
ASSERT_EQ(delegate(), 4);
}
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<&power_of_two>();
ASSERT_EQ(lhs, entt::delegate<int(int)>{entt::connect_arg<&power_of_two>});
ASSERT_TRUE(lhs != rhs);
ASSERT_FALSE(lhs == rhs);
ASSERT_NE(lhs, rhs);
rhs.connect<&power_of_two>();
ASSERT_EQ(rhs, entt::delegate<int(int)>{entt::connect_arg<&power_of_two>});
ASSERT_FALSE(lhs != rhs);
ASSERT_TRUE(lhs == rhs);
ASSERT_EQ(lhs, rhs);
lhs.connect<&sum_with_ref>(value);
ASSERT_EQ(lhs, (entt::delegate<int(int)>{entt::connect_arg<&sum_with_ref>, value}));
ASSERT_TRUE(lhs != rhs);
ASSERT_FALSE(lhs == rhs);
ASSERT_NE(lhs, rhs);
rhs.connect<&sum_with_ref>(value);
ASSERT_EQ(rhs, (entt::delegate<int(int)>{entt::connect_arg<&sum_with_ref>, value}));
ASSERT_FALSE(lhs != rhs);
ASSERT_TRUE(lhs == rhs);
ASSERT_EQ(lhs, rhs);
lhs.connect<&sum_with_ptr>(&value);
ASSERT_EQ(lhs, (entt::delegate<int(int)>{entt::connect_arg<&sum_with_ptr>, &value}));
ASSERT_TRUE(lhs != rhs);
ASSERT_FALSE(lhs == rhs);
ASSERT_NE(lhs, rhs);
rhs.connect<&sum_with_ptr>(&value);
ASSERT_EQ(rhs, (entt::delegate<int(int)>{entt::connect_arg<&sum_with_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_EQ(lhs.target(), rhs.target());
ASSERT_EQ(lhs.data(), rhs.data());
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_EQ(lhs.target(), rhs.target());
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)->mul(val) * val; }, &functor);
ASSERT_NE(lhs, (entt::delegate<int(int)>{[](const void *, int val) { return val + val; }, &functor}));
ASSERT_NE(lhs.target(), rhs.target());
ASSERT_EQ(lhs.data(), rhs.data());
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)->mul(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;
const entt::delegate plain_func{entt::connect_arg<&power_of_two>};
const entt::delegate sum_func_with_ref{entt::connect_arg<&sum_with_ref>, value};
const entt::delegate sum_func_with_const_ref{entt::connect_arg<&sum_with_ref>, std::as_const(value)};
const entt::delegate sum_func_with_ptr{entt::connect_arg<&sum_with_ptr>, &value};
const entt::delegate sum_func_with_const_ptr{entt::connect_arg<&sum_with_ptr>, &std::as_const(value)};
const entt::delegate member_func_f{entt::connect_arg<&const_nonconst_noexcept::f>, functor};
const entt::delegate member_func_g{entt::connect_arg<&const_nonconst_noexcept::g>, functor};
const entt::delegate member_func_h{entt::connect_arg<&const_nonconst_noexcept::h>, &functor};
const entt::delegate member_func_h_const{entt::connect_arg<&const_nonconst_noexcept::h>, &std::as_const(functor)};
const entt::delegate member_func_i{entt::connect_arg<&const_nonconst_noexcept::i>, functor};
const entt::delegate member_func_i_const{entt::connect_arg<&const_nonconst_noexcept::i>, std::as_const(functor)};
const entt::delegate data_member_u{entt::connect_arg<&const_nonconst_noexcept::u>, functor};
const entt::delegate data_member_v{entt::connect_arg<&const_nonconst_noexcept::v>, &functor};
const entt::delegate data_member_v_const{entt::connect_arg<&const_nonconst_noexcept::v>, &std::as_const(functor)};
const entt::delegate lambda{+[](const void *, int) { return 0; }};
testing::StaticAssertTypeEq<typename decltype(plain_func)::type, int(const int &)>();
testing::StaticAssertTypeEq<typename decltype(sum_func_with_ref)::type, int(int)>();
testing::StaticAssertTypeEq<typename decltype(sum_func_with_const_ref)::type, int(int)>();
testing::StaticAssertTypeEq<typename decltype(sum_func_with_ptr)::type, int(int)>();
testing::StaticAssertTypeEq<typename decltype(sum_func_with_const_ptr)::type, int(int)>();
testing::StaticAssertTypeEq<typename decltype(member_func_f)::type, void()>();
testing::StaticAssertTypeEq<typename decltype(member_func_g)::type, void()>();
testing::StaticAssertTypeEq<typename decltype(member_func_h)::type, void()>();
testing::StaticAssertTypeEq<typename decltype(member_func_h_const)::type, void()>();
testing::StaticAssertTypeEq<typename decltype(member_func_i)::type, void()>();
testing::StaticAssertTypeEq<typename decltype(member_func_i_const)::type, void()>();
testing::StaticAssertTypeEq<typename decltype(data_member_u)::type, int()>();
testing::StaticAssertTypeEq<typename decltype(data_member_v)::type, const int()>();
testing::StaticAssertTypeEq<typename decltype(data_member_v_const)::type, const int()>();
testing::StaticAssertTypeEq<typename decltype(lambda)::type, int(int)>();
ASSERT_TRUE(plain_func);
ASSERT_TRUE(sum_func_with_ref);
ASSERT_TRUE(sum_func_with_const_ref);
ASSERT_TRUE(sum_func_with_ptr);
ASSERT_TRUE(sum_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::mul>(functor);
ASSERT_TRUE(delegate);
ASSERT_EQ(delegate(3), 12);
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, DiscardLast) {
entt::delegate<int(int, const std::unique_ptr<int> &)> delegate;
const auto value = 3;
const auto other = std::make_unique<int>(4);
delegate.connect<&power_of_two>();
ASSERT_TRUE(delegate);
ASSERT_EQ(delegate(3, other), 9);
delegate.connect<&sum_with_ref>(value);
ASSERT_TRUE(delegate);
ASSERT_EQ(delegate(1, other), 4);
delegate.connect<&sum_with_ptr>(&value);
ASSERT_TRUE(delegate);
ASSERT_EQ(delegate(2, other), 5);
}
TEST(Delegate, SkipFirst) {
entt::delegate<int(const std::unique_ptr<int> &, int)> delegate;
const auto value = 3;
const auto other = std::make_unique<int>(4);
delegate.connect<&power_of_two>();
ASSERT_TRUE(delegate);
ASSERT_EQ(delegate(other, 3), 9);
delegate.connect<&sum_with_ref>(value);
ASSERT_TRUE(delegate);
ASSERT_EQ(delegate(other, 1), 4);
delegate.connect<&sum_with_ptr>(&value);
ASSERT_TRUE(delegate);
ASSERT_EQ(delegate(other, 2), 5);
}
TEST(Delegate, Constructors) {
delegate_functor functor;
const auto value = 2;
const entt::delegate<int(int)> empty{};
const entt::delegate<int(int)> func{entt::connect_arg<&power_of_two>};
const entt::delegate<int(int)> ref{entt::connect_arg<&sum_with_ref>, value};
const entt::delegate<int(int)> ptr{entt::connect_arg<&sum_with_ptr>, &value};
const 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(5, ptr(3));
ASSERT_TRUE(member);
ASSERT_EQ(6, member(3));
}
TEST(Delegate, VoidVsNonVoidReturnType) {
delegate_functor functor;
const entt::delegate<void(int)> func{entt::connect_arg<&power_of_two>};
const entt::delegate<void(int)> member{entt::connect_arg<&delegate_functor::operator()>, &functor};
const entt::delegate<void(int)> cmember{entt::connect_arg<&delegate_functor::mul>, &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>();
const delegate_functor functor;
ASSERT_EQ(delegate(functor), 4);
}
TEST(Delegate, UnboundMemberFunction) {
entt::delegate<int(delegate_functor *, const int &)> 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 power_of_two(const int &);
bound.connect<&power_of_two>();
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::mul>(&functor);
ASSERT_EQ(bound(3, 'c'), 12);
// int delegate_functor::operator()(int);
unbound.connect<&delegate_functor::operator()>();
ASSERT_EQ(unbound(functor, 3, 'c'), 6);
}

View File

@@ -0,0 +1,198 @@
#include <memory>
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/signal/dispatcher.hpp>
#include "../../common/empty.h"
// makes the type non-aggregate
struct non_aggregate {
non_aggregate(int) {}
};
struct receiver {
static void forward(entt::dispatcher &dispatcher, test::empty &event) {
dispatcher.enqueue(event);
}
void receive(const test::empty &) {
++cnt;
}
void reset() {
cnt = 0;
}
int cnt{0};
};
TEST(Dispatcher, Functionalities) {
entt::dispatcher dispatcher{};
entt::dispatcher other{std::move(dispatcher)};
dispatcher = std::move(other);
receiver receiver{};
ASSERT_EQ(dispatcher.size<test::empty>(), 0u);
ASSERT_EQ(dispatcher.size(), 0u);
dispatcher.trigger(non_aggregate{1});
dispatcher.enqueue<non_aggregate>(2);
dispatcher.update<non_aggregate>();
dispatcher.sink<test::empty>().connect<&receiver::receive>(receiver);
dispatcher.trigger<test::empty>();
dispatcher.enqueue<test::empty>();
ASSERT_EQ(dispatcher.size<non_aggregate>(), 0u);
ASSERT_EQ(dispatcher.size<test::empty>(), 1u);
ASSERT_EQ(dispatcher.size(), 1u);
ASSERT_EQ(receiver.cnt, 1);
dispatcher.enqueue(test::other_empty{});
dispatcher.update<test::other_empty>();
ASSERT_EQ(dispatcher.size<test::other_empty>(), 0u);
ASSERT_EQ(dispatcher.size<test::empty>(), 1u);
ASSERT_EQ(dispatcher.size(), 1u);
ASSERT_EQ(receiver.cnt, 1);
dispatcher.update<test::empty>();
dispatcher.trigger<test::empty>();
ASSERT_EQ(dispatcher.size<test::empty>(), 0u);
ASSERT_EQ(dispatcher.size(), 0u);
ASSERT_EQ(receiver.cnt, 3);
dispatcher.enqueue<test::empty>();
dispatcher.clear<test::empty>();
dispatcher.update();
dispatcher.enqueue(test::empty{});
dispatcher.clear();
dispatcher.update();
ASSERT_EQ(dispatcher.size<test::empty>(), 0u);
ASSERT_EQ(dispatcher.size(), 0u);
ASSERT_EQ(receiver.cnt, 3);
receiver.reset();
test::empty event{};
dispatcher.sink<test::empty>().disconnect<&receiver::receive>(receiver);
dispatcher.trigger<test::empty>();
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<test::empty>().connect<&receiver::receive>(receiver);
dispatcher.enqueue<test::empty>();
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<test::empty>().connect<&receiver::forward>(dispatcher);
dispatcher.sink<test::empty>().connect<&receiver::receive>(receiver);
dispatcher.enqueue<test::empty>();
dispatcher.update();
ASSERT_EQ(receiver.cnt, 1);
dispatcher.sink<test::empty>().disconnect<&receiver::forward>(dispatcher);
dispatcher.update();
ASSERT_EQ(receiver.cnt, 2);
}
TEST(Dispatcher, OpaqueDisconnect) {
entt::dispatcher dispatcher{};
receiver receiver{};
dispatcher.sink<test::empty>().connect<&receiver::receive>(receiver);
dispatcher.trigger<test::empty>();
ASSERT_EQ(receiver.cnt, 1);
dispatcher.disconnect(receiver);
dispatcher.trigger<test::empty>();
ASSERT_EQ(receiver.cnt, 1);
}
TEST(Dispatcher, NamedQueue) {
using namespace entt::literals;
entt::dispatcher dispatcher{};
receiver receiver{};
dispatcher.sink<test::empty>("named"_hs).connect<&receiver::receive>(receiver);
dispatcher.trigger<test::empty>();
ASSERT_EQ(receiver.cnt, 0);
dispatcher.trigger("named"_hs, test::empty{});
ASSERT_EQ(receiver.cnt, 1);
dispatcher.enqueue<test::empty>();
dispatcher.enqueue(test::empty{});
dispatcher.enqueue_hint<test::empty>("named"_hs);
dispatcher.enqueue_hint("named"_hs, test::empty{});
dispatcher.update<test::empty>();
ASSERT_EQ(receiver.cnt, 1);
dispatcher.clear<test::empty>();
dispatcher.update<test::empty>("named"_hs);
ASSERT_EQ(receiver.cnt, 3);
dispatcher.enqueue_hint<test::empty>("named"_hs);
dispatcher.clear<test::empty>("named"_hs);
dispatcher.update<test::empty>("named"_hs);
ASSERT_EQ(receiver.cnt, 3);
}
TEST(Dispatcher, CustomAllocator) {
const std::allocator<void> allocator{};
entt::dispatcher dispatcher{allocator};
ASSERT_EQ(dispatcher.get_allocator(), allocator);
ASSERT_FALSE(dispatcher.get_allocator() != allocator);
dispatcher.enqueue<test::empty>();
const decltype(dispatcher) other{std::move(dispatcher), allocator};
ASSERT_EQ(other.size<test::empty>(), 1u);
}

View File

@@ -0,0 +1,169 @@
#include <functional>
#include <memory>
#include <utility>
#include <gtest/gtest.h>
#include <entt/signal/emitter.hpp>
#include "../../common/boxed_type.h"
#include "../../common/emitter.h"
#include "../../common/empty.h"
#include "../../common/linter.hpp"
TEST(Emitter, Move) {
test::emitter emitter{};
emitter.on<test::boxed_int>([](auto &, const auto &) {});
ASSERT_FALSE(emitter.empty());
ASSERT_TRUE(emitter.contains<test::boxed_int>());
test::emitter other{std::move(emitter)};
test::is_initialized(emitter);
ASSERT_FALSE(other.empty());
ASSERT_TRUE(other.contains<test::boxed_int>());
ASSERT_TRUE(emitter.empty());
emitter = std::move(other);
test::is_initialized(other);
ASSERT_FALSE(emitter.empty());
ASSERT_TRUE(emitter.contains<test::boxed_int>());
ASSERT_TRUE(other.empty());
}
TEST(Emitter, Swap) {
test::emitter emitter{};
test::emitter other{};
int value{};
emitter.on<test::boxed_int>([&value](auto &event, const auto &) {
value = event.value;
});
ASSERT_FALSE(emitter.empty());
ASSERT_TRUE(other.empty());
emitter.swap(other);
emitter.publish(test::boxed_int{1});
ASSERT_EQ(value, 0);
ASSERT_TRUE(emitter.empty());
ASSERT_FALSE(other.empty());
other.publish(test::boxed_int{1});
ASSERT_EQ(value, 1);
}
TEST(Emitter, Clear) {
test::emitter emitter{};
ASSERT_TRUE(emitter.empty());
emitter.on<test::boxed_int>([](auto &, const auto &) {});
emitter.on<test::boxed_char>([](const auto &, const auto &) {});
ASSERT_FALSE(emitter.empty());
ASSERT_TRUE(emitter.contains<test::boxed_int>());
ASSERT_TRUE(emitter.contains<test::boxed_char>());
ASSERT_FALSE(emitter.contains<test::empty>());
emitter.erase<test::empty>();
ASSERT_FALSE(emitter.empty());
ASSERT_TRUE(emitter.contains<test::boxed_int>());
ASSERT_TRUE(emitter.contains<test::boxed_char>());
ASSERT_FALSE(emitter.contains<test::empty>());
emitter.erase<test::boxed_int>();
ASSERT_FALSE(emitter.empty());
ASSERT_FALSE(emitter.contains<test::boxed_int>());
ASSERT_TRUE(emitter.contains<test::boxed_char>());
ASSERT_FALSE(emitter.contains<test::empty>());
emitter.on<test::boxed_int>([](auto &, const auto &) {});
emitter.on<test::empty>([](const auto &, const auto &) {});
ASSERT_FALSE(emitter.empty());
ASSERT_TRUE(emitter.contains<test::boxed_int>());
ASSERT_TRUE(emitter.contains<test::boxed_char>());
ASSERT_TRUE(emitter.contains<test::empty>());
emitter.clear();
ASSERT_TRUE(emitter.empty());
ASSERT_FALSE(emitter.contains<test::boxed_int>());
ASSERT_FALSE(emitter.contains<test::empty>());
}
TEST(Emitter, ClearFromCallback) {
test::emitter emitter{};
ASSERT_TRUE(emitter.empty());
emitter.on<test::boxed_int>([](auto &, auto &owner) {
owner.template on<test::boxed_int>([](auto &, auto &) {});
owner.template erase<test::boxed_int>();
});
emitter.on<test::empty>([](const auto &, auto &owner) {
owner.template on<test::empty>([](const auto &, auto &) {});
owner.template erase<test::empty>();
});
ASSERT_FALSE(emitter.empty());
emitter.publish(test::boxed_int{});
emitter.publish(test::empty{});
ASSERT_TRUE(emitter.empty());
}
TEST(Emitter, On) {
test::emitter emitter{};
int value{};
emitter.on<test::boxed_int>([&value](auto &event, const auto &) {
value = event.value;
});
ASSERT_FALSE(emitter.empty());
ASSERT_TRUE(emitter.contains<test::boxed_int>());
ASSERT_EQ(value, 0);
emitter.publish(test::boxed_int{1});
ASSERT_EQ(value, 1);
}
TEST(Emitter, OnAndErase) {
test::emitter emitter{};
const std::function<void(test::empty &, test::emitter &)> func{};
emitter.on(func);
ASSERT_FALSE(emitter.empty());
ASSERT_TRUE(emitter.contains<test::empty>());
emitter.erase<test::empty>();
ASSERT_TRUE(emitter.empty());
ASSERT_FALSE(emitter.contains<test::empty>());
}
TEST(Emitter, CustomAllocator) {
const std::allocator<void> allocator{};
test::emitter emitter{allocator};
ASSERT_EQ(emitter.get_allocator(), allocator);
ASSERT_FALSE(emitter.get_allocator() != allocator);
emitter.on<test::boxed_int>([](auto &, const auto &) {});
const decltype(emitter) other{std::move(emitter), allocator};
test::is_initialized(emitter);
ASSERT_TRUE(emitter.empty());
ASSERT_FALSE(other.empty());
}

View File

@@ -0,0 +1,554 @@
#include <memory>
#include <utility>
#include <gtest/gtest.h>
#include <entt/signal/sigh.hpp>
#include "../../common/config.h"
#include "../../common/linter.hpp"
struct sigh_listener {
static void f(int &iv) {
++iv;
}
[[nodiscard]] bool g(int) {
val = !val;
return true;
}
[[nodiscard]] bool h(const int &) const {
return val;
}
// useless definition just because msvc does weird things if both are empty
void i() {
val = val && val;
}
bool val{false};
};
struct const_nonconst_noexcept {
void f() {
++cnt;
}
void g() noexcept {
++cnt;
}
void h() const {
++cnt;
}
void i() const noexcept {
++cnt;
}
mutable int cnt{0};
};
void connect_and_auto_disconnect(entt::sigh<void(int &)> &sigh, const int &) {
entt::sink sink{sigh};
sink.connect<sigh_listener::f>();
sink.disconnect<&connect_and_auto_disconnect>(sigh);
}
ENTT_DEBUG_TEST(SinkDeathTest, Invalid) {
sigh_listener listener;
entt::sigh<void(int &)> sigh;
entt::sink<entt::sigh<void(int &)>> sink{};
ASSERT_FALSE(sink);
ASSERT_DEATH([[maybe_unused]] const bool empty = sink.empty(), "");
ASSERT_DEATH(sink.connect<&sigh_listener::f>(), "");
ASSERT_DEATH(sink.disconnect<&sigh_listener::f>(), "");
ASSERT_DEATH(sink.disconnect(&listener), "");
ASSERT_DEATH(sink.disconnect(), "");
sink = entt::sink{sigh};
ASSERT_TRUE(sink);
ASSERT_TRUE(sink.empty());
}
TEST(SigH, Lifetime) {
using signal = entt::sigh<void(void)>;
ASSERT_NO_THROW(signal{});
signal src{};
signal other{};
ASSERT_NO_THROW(signal{src});
ASSERT_NO_THROW(signal{std::move(other)});
other = {};
ASSERT_NO_THROW(src = other);
ASSERT_NO_THROW(src = std::move(other));
ASSERT_NO_THROW(delete new signal{});
}
TEST(SigH, Disconnect) {
sigh_listener listener;
entt::sigh<void(int &)> sigh;
entt::sink sink{sigh};
sink.connect<&sigh_listener::f>();
ASSERT_FALSE(sink.empty());
ASSERT_FALSE(sigh.empty());
sink.disconnect<&sigh_listener::f>();
ASSERT_TRUE(sink.empty());
ASSERT_TRUE(sigh.empty());
sink.connect<&sigh_listener::g>(listener);
ASSERT_FALSE(sink.empty());
ASSERT_FALSE(sigh.empty());
sink.disconnect<&sigh_listener::g>(listener);
ASSERT_TRUE(sink.empty());
ASSERT_TRUE(sigh.empty());
sink.connect<&sigh_listener::g>(listener);
ASSERT_FALSE(sink.empty());
ASSERT_FALSE(sigh.empty());
sink.disconnect(&listener);
ASSERT_TRUE(sink.empty());
ASSERT_TRUE(sigh.empty());
sink.connect<&sigh_listener::f>();
ASSERT_FALSE(sink.empty());
ASSERT_FALSE(sigh.empty());
sink.disconnect();
ASSERT_TRUE(sink.empty());
ASSERT_TRUE(sigh.empty());
}
TEST(SigH, Swap) {
entt::sigh<void(int &)> sigh1;
entt::sigh<void(int &)> sigh2;
entt::sink sink1{sigh1};
const 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(SigH, Functions) {
entt::sigh<void(int &)> sigh;
entt::sink sink{sigh};
int value = 0;
sink.connect<&sigh_listener::f>();
sigh.publish(value);
ASSERT_FALSE(sigh.empty());
ASSERT_EQ(sigh.size(), 1u);
ASSERT_EQ(value, 1);
value = 0;
sink.disconnect<&sigh_listener::f>();
sigh.publish(value);
ASSERT_TRUE(sigh.empty());
ASSERT_EQ(sigh.size(), 0u);
ASSERT_EQ(value, 0);
}
TEST(SigH, FunctionsWithPayload) {
entt::sigh<void()> sigh;
entt::sink sink{sigh};
int value = 0;
sink.connect<&sigh_listener::f>(value);
sigh.publish();
ASSERT_FALSE(sigh.empty());
ASSERT_EQ(sigh.size(), 1u);
ASSERT_EQ(value, 1);
value = 0;
sink.disconnect<&sigh_listener::f>(value);
sigh.publish();
ASSERT_TRUE(sigh.empty());
ASSERT_EQ(sigh.size(), 0u);
ASSERT_EQ(value, 0);
sink.connect<&sigh_listener::f>(value);
sink.disconnect(&value);
sigh.publish();
ASSERT_EQ(value, 0);
}
TEST(SigH, Members) {
sigh_listener l1;
sigh_listener l2;
entt::sigh<bool(int)> sigh;
entt::sink sink{sigh};
sink.connect<&sigh_listener::g>(l1);
sigh.publish(3);
ASSERT_TRUE(l1.val);
ASSERT_FALSE(sigh.empty());
ASSERT_EQ(sigh.size(), 1u);
sink.disconnect<&sigh_listener::g>(l1);
sigh.publish(3);
ASSERT_TRUE(l1.val);
ASSERT_TRUE(sigh.empty());
ASSERT_EQ(sigh.size(), 0u);
sink.connect<&sigh_listener::g>(&l1);
sink.connect<&sigh_listener::h>(l2);
ASSERT_FALSE(sigh.empty());
ASSERT_EQ(sigh.size(), 2u);
sink.disconnect(&l1);
ASSERT_FALSE(sigh.empty());
ASSERT_EQ(sigh.size(), 1u);
}
TEST(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.val = true;
++cnt;
};
listener.val = true;
sigh.collect(std::move(no_return), 3);
ASSERT_FALSE(sigh.empty());
ASSERT_EQ(cnt, 2);
auto bool_return = [&cnt](bool value) {
// gtest and its macro hell are sometimes really annoying...
[](auto curr) { ASSERT_TRUE(curr); }(value);
++cnt;
return true;
};
cnt = 0;
sigh.collect(std::move(bool_return), 3);
ASSERT_EQ(cnt, 1);
}
TEST(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; }, 3);
ASSERT_FALSE(sigh.empty());
ASSERT_EQ(cnt, 2);
cnt = 0;
sigh.collect([&cnt]() { ++cnt; return true; }, 3);
ASSERT_EQ(cnt, 1);
}
TEST(SigH, Connection) {
entt::sigh<void(int &)> sigh;
entt::sink sink{sigh};
int value = 0;
auto conn = sink.connect<&sigh_listener::f>();
sigh.publish(value);
ASSERT_FALSE(sigh.empty());
ASSERT_TRUE(conn);
ASSERT_EQ(value, 1);
value = 0;
conn.release();
sigh.publish(value);
ASSERT_TRUE(sigh.empty());
ASSERT_FALSE(conn);
ASSERT_EQ(0, value);
}
TEST(SigH, ScopedConnection) {
sigh_listener listener;
entt::sigh<void(int)> sigh;
entt::sink sink{sigh};
{
ASSERT_FALSE(listener.val);
const entt::scoped_connection conn = sink.connect<&sigh_listener::g>(listener);
sigh.publish(1);
ASSERT_FALSE(sigh.empty());
ASSERT_TRUE(listener.val);
ASSERT_TRUE(conn);
}
sigh.publish(1);
ASSERT_TRUE(sigh.empty());
ASSERT_TRUE(listener.val);
}
TEST(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);
{
const entt::scoped_connection inner{std::move(outer)};
test::is_initialized(outer);
ASSERT_FALSE(listener.val);
ASSERT_FALSE(outer);
ASSERT_TRUE(inner);
sigh.publish(1);
ASSERT_TRUE(listener.val);
}
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.val);
ASSERT_TRUE(outer);
ASSERT_FALSE(inner);
inner = std::move(outer);
test::is_initialized(outer);
ASSERT_FALSE(outer);
ASSERT_TRUE(inner);
sigh.publish(1);
ASSERT_FALSE(listener.val);
}
ASSERT_TRUE(sigh.empty());
}
TEST(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.val);
ASSERT_FALSE(inner);
inner = sink.connect<&sigh_listener::g>(listener);
sigh.publish(1);
ASSERT_FALSE(sigh.empty());
ASSERT_TRUE(listener.val);
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(1);
ASSERT_FALSE(sigh.empty());
ASSERT_FALSE(listener.val);
ASSERT_TRUE(inner);
}
sigh.publish(1);
ASSERT_TRUE(sigh.empty());
ASSERT_FALSE(listener.val);
}
TEST(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(SigH, UnboundDataMember) {
sigh_listener listener;
entt::sigh<bool &(sigh_listener &)> sigh;
entt::sink sink{sigh};
ASSERT_FALSE(listener.val);
sink.connect<&sigh_listener::val>();
sigh.collect([](bool &value) { value = !value; }, listener);
ASSERT_TRUE(listener.val);
}
TEST(SigH, UnboundMemberFunction) {
sigh_listener listener;
entt::sigh<void(sigh_listener *, int)> sigh;
entt::sink sink{sigh};
ASSERT_FALSE(listener.val);
sink.connect<&sigh_listener::g>();
sigh.publish(&listener, 1);
ASSERT_TRUE(listener.val);
}
TEST(SigH, ConnectAndAutoDisconnect) {
sigh_listener listener;
entt::sigh<void(int &)> sigh;
entt::sink sink{sigh};
int value = 0;
sink.connect<&sigh_listener::g>(listener);
sink.connect<&connect_and_auto_disconnect>(sigh);
ASSERT_FALSE(listener.val);
ASSERT_EQ(sigh.size(), 2u);
ASSERT_EQ(value, 0);
sigh.publish(value);
ASSERT_TRUE(listener.val);
ASSERT_EQ(sigh.size(), 2u);
ASSERT_EQ(value, 0);
sigh.publish(value);
ASSERT_FALSE(listener.val);
ASSERT_EQ(sigh.size(), 2u);
ASSERT_EQ(value, 1);
}
TEST(SigH, CustomAllocator) {
const std::allocator<void (*)(int)> allocator;
entt::sigh<void(int), std::allocator<void (*)(int)>> 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};
test::is_initialized(copy);
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());
}