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

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

View File

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

View File

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

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

File diff suppressed because it is too large Load Diff

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

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

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

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

File diff suppressed because it is too large Load Diff

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

File diff suppressed because it is too large Load Diff

View File

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

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

File diff suppressed because it is too large Load Diff