#include #include #include #include #include #include #include #include #include #include #include #include #include #include "../common/config.h" struct empty_type {}; struct no_eto_type { static constexpr std::size_t page_size = 1024u; }; bool operator==(const no_eto_type &lhs, const no_eto_type &rhs) { return &lhs == &rhs; } struct stable_type { static constexpr auto in_place_delete = true; int value; }; struct non_default_constructible { non_default_constructible(int v) : value{v} {} int value; }; struct aggregate { int value{}; }; struct listener { template static void sort(entt::registry ®istry) { registry.sort([](auto lhs, auto rhs) { return lhs < rhs; }); } void incr(const entt::registry &, entt::entity entity) { last = entity; ++counter; } void decr(const entt::registry &, entt::entity entity) { last = entity; --counter; } entt::entity last{entt::null}; int counter{0}; }; struct owner { void receive(const entt::registry &ref) { parent = &ref; } const entt::registry *parent{nullptr}; }; struct destruction_order { using ctx_check_type = int; destruction_order(const entt::registry &ref, bool &ctx) : registry{&ref}, ctx_check{&ctx} { *ctx_check = (registry->ctx().find() != nullptr); } ~destruction_order() { *ctx_check = *ctx_check && (registry->ctx().find() != nullptr); } private: const entt::registry *registry; bool *ctx_check{}; }; enum class small_entity : std::uint32_t {}; struct small_entity_traits { using value_type = small_entity; using entity_type = uint32_t; using version_type = uint16_t; static constexpr entity_type entity_mask = 0xFF; static constexpr entity_type version_mask = 0x00; }; template<> struct entt::entt_traits: entt::basic_entt_traits { using base_type = entt::basic_entt_traits; static constexpr auto page_size = ENTT_SPARSE_PAGE; }; TEST(Registry, Context) { entt::registry registry; auto &ctx = registry.ctx(); const auto &cctx = std::as_const(registry).ctx(); ASSERT_FALSE(ctx.contains()); ASSERT_FALSE(cctx.contains()); ASSERT_EQ(ctx.find(), nullptr); ASSERT_EQ(cctx.find(), nullptr); ctx.emplace(); ctx.emplace(); ASSERT_TRUE(ctx.contains()); ASSERT_TRUE(cctx.contains()); ASSERT_NE(ctx.find(), nullptr); ASSERT_NE(cctx.find(), nullptr); ASSERT_FALSE(ctx.erase()); ASSERT_TRUE(ctx.erase()); ASSERT_TRUE(ctx.contains()); ASSERT_FALSE(cctx.contains()); ASSERT_NE(ctx.find(), nullptr); ASSERT_EQ(cctx.find(), nullptr); ASSERT_FALSE(ctx.erase()); ASSERT_TRUE(ctx.erase()); ctx.emplace('c'); ctx.emplace(42); ASSERT_EQ(ctx.emplace('a'), 'c'); ASSERT_EQ(ctx.find(), cctx.find()); ASSERT_EQ(ctx.get(), cctx.get()); ASSERT_EQ(ctx.get(), 'c'); ASSERT_EQ(ctx.emplace(0), 42); ASSERT_EQ(ctx.find(), cctx.find()); ASSERT_EQ(ctx.get(), cctx.get()); ASSERT_EQ(ctx.get(), 42); ASSERT_EQ(ctx.find(), nullptr); ASSERT_EQ(cctx.find(), nullptr); ASSERT_EQ(ctx.insert_or_assign('a'), 'a'); ASSERT_EQ(ctx.find(), cctx.find()); ASSERT_EQ(ctx.get(), cctx.get()); ASSERT_EQ(ctx.get(), 'a'); ASSERT_EQ(ctx.insert_or_assign(0), 0); ASSERT_EQ(ctx.find(), cctx.find()); ASSERT_EQ(ctx.get(), cctx.get()); ASSERT_EQ(ctx.get(), 0); } TEST(Registry, ContextHint) { using namespace entt::literals; entt::registry registry; auto &ctx = registry.ctx(); const auto &cctx = std::as_const(registry).ctx(); ctx.emplace(42); ctx.emplace_as("other"_hs, 3); ASSERT_TRUE(ctx.contains()); ASSERT_TRUE(cctx.contains("other"_hs)); ASSERT_FALSE(ctx.contains("other"_hs)); ASSERT_NE(cctx.find(), nullptr); ASSERT_NE(ctx.find("other"_hs), nullptr); ASSERT_EQ(cctx.find("other"_hs), nullptr); ASSERT_EQ(ctx.get(), 42); ASSERT_EQ(cctx.get("other"_hs), 3); ctx.insert_or_assign(3); ctx.insert_or_assign("other"_hs, 42); ASSERT_EQ(ctx.get(), 3); ASSERT_EQ(cctx.get("other"_hs), 42); ASSERT_FALSE(ctx.erase("other"_hs)); ASSERT_TRUE(ctx.erase()); ASSERT_TRUE(cctx.contains("other"_hs)); ASSERT_EQ(ctx.get("other"_hs), 42); ASSERT_TRUE(ctx.erase("other"_hs)); ASSERT_FALSE(cctx.contains("other"_hs)); ASSERT_EQ(ctx.find("other"_hs), nullptr); } TEST(Registry, ContextAsRef) { entt::registry registry; int value{3}; registry.ctx().emplace(value); ASSERT_NE(registry.ctx().find(), nullptr); ASSERT_NE(registry.ctx().find(), nullptr); ASSERT_NE(std::as_const(registry).ctx().find(), nullptr); ASSERT_EQ(registry.ctx().get(), 3); ASSERT_EQ(registry.ctx().get(), 3); registry.ctx().get() = 42; ASSERT_EQ(registry.ctx().get(), 42); ASSERT_EQ(value, 42); value = 3; ASSERT_EQ(std::as_const(registry).ctx().get(), 3); } TEST(Registry, ContextAsConstRef) { entt::registry registry; int value{3}; registry.ctx().emplace(value); ASSERT_EQ(registry.ctx().find(), nullptr); ASSERT_NE(registry.ctx().find(), nullptr); ASSERT_NE(std::as_const(registry).ctx().find(), nullptr); ASSERT_EQ(registry.ctx().get(), 3); value = 42; ASSERT_EQ(std::as_const(registry).ctx().get(), 42); } TEST(Registry, Functionalities) { using traits_type = entt::entt_traits; entt::registry registry; ASSERT_NO_FATAL_FAILURE([[maybe_unused]] auto alloc = registry.get_allocator()); ASSERT_EQ(registry.size(), 0u); ASSERT_EQ(registry.alive(), 0u); ASSERT_NO_FATAL_FAILURE(registry.reserve(42)); ASSERT_EQ(registry.capacity(), 42u); ASSERT_TRUE(registry.empty()); ASSERT_EQ(registry.storage().size(), 0u); ASSERT_EQ(registry.storage().size(), 0u); ASSERT_TRUE(registry.storage().empty()); ASSERT_TRUE(registry.storage().empty()); const auto e0 = registry.create(); const auto e1 = registry.create(); registry.emplace(e1); registry.emplace(e1); ASSERT_TRUE(registry.all_of<>(e0)); ASSERT_FALSE(registry.any_of<>(e1)); ASSERT_EQ(registry.storage().size(), 1u); ASSERT_EQ(registry.storage().size(), 1u); ASSERT_FALSE(registry.storage().empty()); ASSERT_FALSE(registry.storage().empty()); ASSERT_NE(e0, e1); ASSERT_FALSE((registry.all_of(e0))); ASSERT_TRUE((registry.all_of(e1))); ASSERT_FALSE((registry.any_of(e0))); ASSERT_TRUE((registry.any_of(e1))); ASSERT_EQ(registry.try_get(e0), nullptr); ASSERT_NE(registry.try_get(e1), nullptr); ASSERT_EQ(registry.try_get(e0), nullptr); ASSERT_NE(registry.try_get(e1), nullptr); ASSERT_EQ(registry.try_get(e0), nullptr); ASSERT_EQ(registry.try_get(e1), nullptr); ASSERT_EQ(registry.emplace(e0, 42), 42); ASSERT_EQ(registry.emplace(e0, 'c'), 'c'); ASSERT_NO_FATAL_FAILURE(registry.erase(e1)); ASSERT_NO_FATAL_FAILURE(registry.erase(e1)); ASSERT_TRUE((registry.all_of(e0))); ASSERT_FALSE((registry.all_of(e1))); ASSERT_TRUE((registry.any_of(e0))); ASSERT_FALSE((registry.any_of(e1))); const auto e2 = registry.create(); registry.emplace_or_replace(e2, registry.get(e0)); registry.emplace_or_replace(e2, registry.get(e0)); ASSERT_TRUE((registry.all_of(e2))); ASSERT_EQ(registry.get(e0), 42); ASSERT_EQ(registry.get(e0), 'c'); ASSERT_NE(registry.try_get(e0), nullptr); ASSERT_NE(registry.try_get(e0), nullptr); ASSERT_EQ(registry.try_get(e0), nullptr); ASSERT_EQ(*registry.try_get(e0), 42); ASSERT_EQ(*registry.try_get(e0), 'c'); ASSERT_EQ(std::get<0>(registry.get(e0)), 42); ASSERT_EQ(*std::get<0>(registry.try_get(e0)), 42); ASSERT_EQ(std::get<1>(static_cast(registry).get(e0)), 'c'); ASSERT_EQ(*std::get<1>(static_cast(registry).try_get(e0)), 'c'); ASSERT_EQ(registry.get(e0), registry.get(e2)); ASSERT_EQ(registry.get(e0), registry.get(e2)); ASSERT_NE(®istry.get(e0), ®istry.get(e2)); ASSERT_NE(®istry.get(e0), ®istry.get(e2)); ASSERT_EQ(registry.patch(e0, [](auto &instance) { instance = 2; }), 2); ASSERT_EQ(registry.replace(e0, 3), 3); ASSERT_NO_FATAL_FAILURE(registry.emplace_or_replace(e0, 1)); ASSERT_NO_FATAL_FAILURE(registry.emplace_or_replace(e1, 1)); ASSERT_EQ(static_cast(registry).get(e0), 1); ASSERT_EQ(static_cast(registry).get(e1), 1); ASSERT_EQ(registry.size(), 3u); ASSERT_EQ(registry.alive(), 3u); ASSERT_FALSE(registry.empty()); ASSERT_EQ(traits_type::to_version(e2), 0u); ASSERT_EQ(registry.current(e2), 0u); ASSERT_NO_FATAL_FAILURE(registry.destroy(e2)); ASSERT_EQ(traits_type::to_version(e2), 0u); ASSERT_EQ(registry.current(e2), 1u); ASSERT_TRUE(registry.valid(e0)); ASSERT_TRUE(registry.valid(e1)); ASSERT_FALSE(registry.valid(e2)); ASSERT_EQ(registry.size(), 3u); ASSERT_EQ(registry.alive(), 2u); ASSERT_FALSE(registry.empty()); ASSERT_NO_FATAL_FAILURE(registry.clear()); ASSERT_EQ(registry.size(), 3u); ASSERT_EQ(registry.alive(), 0u); ASSERT_TRUE(registry.empty()); const auto e3 = registry.create(); ASSERT_EQ(registry.get_or_emplace(e3, 3), 3); ASSERT_EQ(registry.get_or_emplace(e3, 'c'), 'c'); ASSERT_EQ(registry.storage().size(), 1u); ASSERT_EQ(registry.storage().size(), 1u); ASSERT_FALSE(registry.storage().empty()); ASSERT_FALSE(registry.storage().empty()); ASSERT_TRUE((registry.all_of(e3))); ASSERT_EQ(registry.get(e3), 3); ASSERT_EQ(registry.get(e3), 'c'); ASSERT_NO_FATAL_FAILURE(registry.clear()); ASSERT_EQ(registry.storage().size(), 0u); ASSERT_EQ(registry.storage().size(), 1u); ASSERT_TRUE(registry.storage().empty()); ASSERT_FALSE(registry.storage().empty()); ASSERT_NO_FATAL_FAILURE(registry.clear()); ASSERT_EQ(registry.storage().size(), 0u); ASSERT_EQ(registry.storage().size(), 0u); ASSERT_TRUE(registry.storage().empty()); ASSERT_TRUE(registry.storage().empty()); const auto e4 = registry.create(); const auto e5 = registry.create(); registry.emplace(e4); ASSERT_EQ(registry.remove(e4), 1u); ASSERT_EQ(registry.remove(e5), 0u); ASSERT_EQ(registry.storage().size(), 0u); ASSERT_EQ(registry.storage().size(), 0u); ASSERT_TRUE(registry.storage().empty()); } TEST(Registry, Constructors) { entt::registry registry; entt::registry other{42}; ASSERT_TRUE(registry.empty()); ASSERT_TRUE(other.empty()); ASSERT_EQ(registry.released(), 0u); ASSERT_EQ(other.released(), 0u); } TEST(Registry, Move) { entt::registry registry; const auto entity = registry.create(); owner test{}; registry.on_construct().connect<&owner::receive>(test); registry.on_destroy().connect<&owner::receive>(test); ASSERT_EQ(test.parent, nullptr); registry.emplace(entity); ASSERT_EQ(test.parent, ®istry); entt::registry other{std::move(registry)}; other.erase(entity); registry = {}; registry.emplace(registry.create(entity)); ASSERT_EQ(test.parent, &other); registry = std::move(other); registry.emplace(entity); registry.emplace(registry.create(entity)); ASSERT_EQ(test.parent, ®istry); } TEST(Registry, Swap) { entt::registry registry; const auto entity = registry.create(); owner test{}; registry.on_construct().connect<&owner::receive>(test); registry.on_destroy().connect<&owner::receive>(test); ASSERT_EQ(test.parent, nullptr); registry.emplace(entity); ASSERT_EQ(test.parent, ®istry); entt::registry other; other.swap(registry); other.erase(entity); registry = {}; registry.emplace(registry.create(entity)); ASSERT_EQ(test.parent, &other); registry.swap(other); registry.emplace(entity); registry.emplace(registry.create(entity)); ASSERT_EQ(test.parent, ®istry); } TEST(Registry, ReplaceAggregate) { entt::registry registry; const auto entity = registry.create(); registry.emplace(entity, 0); auto &instance = registry.replace(entity, 42); ASSERT_EQ(instance.value, 42); } TEST(Registry, EmplaceOrReplaceAggregate) { entt::registry registry; const auto entity = registry.create(); auto &instance = registry.emplace_or_replace(entity, 42); ASSERT_EQ(instance.value, 42); } TEST(Registry, Identifiers) { using traits_type = entt::entt_traits; entt::registry registry; const auto pre = registry.create(); ASSERT_EQ(traits_type::to_integral(pre), traits_type::to_entity(pre)); registry.release(pre); const auto post = registry.create(); ASSERT_NE(pre, post); ASSERT_EQ(traits_type::to_entity(pre), traits_type::to_entity(post)); ASSERT_NE(traits_type::to_version(pre), traits_type::to_version(post)); ASSERT_NE(traits_type::to_version(pre), registry.current(pre)); ASSERT_EQ(traits_type::to_version(post), registry.current(post)); const auto invalid = traits_type::combine(traits_type::to_entity(post) + 1u, {}); ASSERT_EQ(traits_type::to_version(invalid), typename traits_type::version_type{}); ASSERT_EQ(registry.current(invalid), traits_type::to_version(entt::tombstone)); } TEST(Registry, Data) { using traits_type = entt::entt_traits; entt::registry registry; ASSERT_EQ(std::as_const(registry).data(), nullptr); const auto entity = registry.create(); ASSERT_EQ(*std::as_const(registry).data(), entity); const auto other = registry.create(); registry.release(entity); ASSERT_EQ(*std::as_const(registry).data(), other); ASSERT_EQ(*(std::as_const(registry).data() + 1u), traits_type::next(entity)); } TEST(Registry, CreateManyEntitiesAtOnce) { using traits_type = entt::entt_traits; entt::registry registry; entt::entity entities[3]; const auto entity = registry.create(); registry.release(registry.create()); registry.release(entity); registry.release(registry.create()); registry.create(std::begin(entities), std::end(entities)); ASSERT_TRUE(registry.valid(entities[0])); ASSERT_TRUE(registry.valid(entities[1])); ASSERT_TRUE(registry.valid(entities[2])); ASSERT_EQ(traits_type::to_entity(entities[0]), 0u); ASSERT_EQ(traits_type::to_version(entities[0]), 2u); ASSERT_EQ(traits_type::to_entity(entities[1]), 1u); ASSERT_EQ(traits_type::to_version(entities[1]), 1u); ASSERT_EQ(traits_type::to_entity(entities[2]), 2u); ASSERT_EQ(traits_type::to_version(entities[2]), 0u); } TEST(Registry, CreateManyEntitiesAtOnceWithListener) { entt::registry registry; entt::entity entities[3]; listener listener; registry.on_construct().connect<&listener::incr>(listener); registry.create(std::begin(entities), std::end(entities)); registry.insert(std::begin(entities), std::end(entities), 42); registry.insert(std::begin(entities), std::end(entities), 'c'); ASSERT_EQ(registry.get(entities[0]), 42); ASSERT_EQ(registry.get(entities[1]), 'c'); ASSERT_EQ(listener.counter, 3); registry.on_construct().disconnect<&listener::incr>(listener); registry.on_construct().connect<&listener::incr>(listener); registry.create(std::begin(entities), std::end(entities)); registry.insert(std::begin(entities), std::end(entities), 'a'); registry.insert(std::begin(entities), std::end(entities)); ASSERT_TRUE(registry.all_of(entities[0])); ASSERT_EQ(registry.get(entities[2]), 'a'); ASSERT_EQ(listener.counter, 6); } TEST(Registry, CreateWithHint) { using traits_type = entt::entt_traits; entt::registry registry; auto e3 = registry.create(entt::entity{3}); auto e2 = registry.create(entt::entity{3}); ASSERT_EQ(e2, entt::entity{1}); ASSERT_FALSE(registry.valid(entt::entity{0})); ASSERT_FALSE(registry.valid(entt::entity{2})); ASSERT_EQ(e3, entt::entity{3}); registry.release(e2); ASSERT_EQ(traits_type::to_version(e2), 0u); ASSERT_EQ(registry.current(e2), 1u); e2 = registry.create(); auto e1 = registry.create(entt::entity{2}); ASSERT_EQ(traits_type::to_entity(e2), 1u); ASSERT_EQ(traits_type::to_version(e2), 1u); ASSERT_EQ(traits_type::to_entity(e1), 2u); ASSERT_EQ(traits_type::to_version(e1), 0u); registry.release(e1); registry.release(e2); auto e0 = registry.create(entt::entity{0}); ASSERT_EQ(e0, entt::entity{0}); ASSERT_EQ(traits_type::to_version(e0), 0u); } TEST(Registry, CreateClearCycle) { using traits_type = entt::entt_traits; entt::registry registry; entt::entity pre{}, post{}; for(int i = 0; i < 10; ++i) { const auto entity = registry.create(); registry.emplace(entity); } registry.clear(); for(int i = 0; i < 7; ++i) { const auto entity = registry.create(); registry.emplace(entity); if(i == 3) { pre = entity; } } registry.clear(); for(int i = 0; i < 5; ++i) { const auto entity = registry.create(); if(i == 3) { post = entity; } } ASSERT_FALSE(registry.valid(pre)); ASSERT_TRUE(registry.valid(post)); ASSERT_NE(traits_type::to_version(pre), traits_type::to_version(post)); ASSERT_EQ(traits_type::to_version(pre) + 1, traits_type::to_version(post)); ASSERT_EQ(registry.current(pre), registry.current(post)); } TEST(Registry, CreateDestroyReleaseCornerCase) { entt::registry registry; const auto e0 = registry.create(); const auto e1 = registry.create(); registry.destroy(e0); registry.release(e1); registry.each([](auto) { FAIL(); }); ASSERT_EQ(registry.current(e0), 1u); ASSERT_EQ(registry.current(e1), 1u); } ENTT_DEBUG_TEST(RegistryDeathTest, CreateTooManyEntities) { entt::basic_registry registry; std::vector entities(entt::entt_traits::to_entity(entt::null)); registry.create(entities.begin(), entities.end()); ASSERT_DEATH([[maybe_unused]] const auto entity = registry.create(), ""); } TEST(Registry, DestroyVersion) { entt::registry registry; const auto e0 = registry.create(); const auto e1 = registry.create(); ASSERT_EQ(registry.current(e0), 0u); ASSERT_EQ(registry.current(e1), 0u); registry.destroy(e0); registry.destroy(e1, 3); ASSERT_EQ(registry.current(e0), 1u); ASSERT_EQ(registry.current(e1), 3u); } ENTT_DEBUG_TEST(RegistryDeathTest, DestroyVersion) { entt::registry registry; const auto entity = registry.create(); registry.destroy(entity); ASSERT_DEATH(registry.destroy(entity), ""); ASSERT_DEATH(registry.destroy(entity, 3), ""); } TEST(Registry, DestroyRange) { entt::registry registry; const auto iview = registry.view(); const auto icview = registry.view(); entt::entity entities[3u]; registry.create(std::begin(entities), std::end(entities)); registry.emplace(entities[0u]); registry.emplace(entities[0u]); registry.emplace(entities[0u]); registry.emplace(entities[1u]); registry.emplace(entities[1u]); registry.emplace(entities[2u]); ASSERT_TRUE(registry.valid(entities[0u])); ASSERT_TRUE(registry.valid(entities[1u])); ASSERT_TRUE(registry.valid(entities[2u])); registry.destroy(icview.begin(), icview.end()); ASSERT_FALSE(registry.valid(entities[0u])); ASSERT_FALSE(registry.valid(entities[1u])); ASSERT_TRUE(registry.valid(entities[2u])); ASSERT_EQ(registry.storage().size(), 1u); ASSERT_EQ(registry.storage().size(), 0u); ASSERT_EQ(registry.storage().size(), 0u); registry.destroy(iview.begin(), iview.end()); ASSERT_FALSE(registry.valid(entities[2u])); ASSERT_NO_FATAL_FAILURE(registry.destroy(iview.rbegin(), iview.rend())); ASSERT_EQ(iview.size(), 0u); ASSERT_EQ(icview.size_hint(), 0u); ASSERT_EQ(registry.storage().size(), 0u); ASSERT_EQ(registry.storage().size(), 0u); ASSERT_EQ(registry.storage().size(), 0u); registry.create(std::begin(entities), std::end(entities)); registry.insert(std::begin(entities), std::end(entities)); ASSERT_TRUE(registry.valid(entities[0u])); ASSERT_TRUE(registry.valid(entities[1u])); ASSERT_TRUE(registry.valid(entities[2u])); ASSERT_EQ(registry.storage().size(), 3u); registry.destroy(std::begin(entities), std::end(entities)); ASSERT_FALSE(registry.valid(entities[0u])); ASSERT_FALSE(registry.valid(entities[1u])); ASSERT_FALSE(registry.valid(entities[2u])); ASSERT_EQ(registry.storage().size(), 0u); entt::sparse_set managed{}; registry.create(std::begin(entities), std::end(entities)); managed.push(std::begin(entities), std::end(entities)); registry.insert(managed.begin(), managed.end()); ASSERT_TRUE(registry.valid(managed[0u])); ASSERT_TRUE(registry.valid(managed[1u])); ASSERT_TRUE(registry.valid(managed[2u])); ASSERT_EQ(registry.storage().size(), 3u); registry.destroy(managed.begin(), managed.end()); ASSERT_FALSE(registry.valid(managed[0u])); ASSERT_FALSE(registry.valid(managed[1u])); ASSERT_FALSE(registry.valid(managed[2u])); ASSERT_EQ(registry.storage().size(), 0u); } TEST(Registry, StableDestroy) { entt::registry registry; const auto iview = registry.view(); const auto icview = registry.view(); entt::entity entities[3u]; registry.create(std::begin(entities), std::end(entities)); registry.emplace(entities[0u]); registry.emplace(entities[0u]); registry.emplace(entities[0u]); registry.emplace(entities[1u]); registry.emplace(entities[1u]); registry.emplace(entities[2u]); ASSERT_TRUE(registry.valid(entities[0u])); ASSERT_TRUE(registry.valid(entities[1u])); ASSERT_TRUE(registry.valid(entities[2u])); registry.destroy(icview.begin(), icview.end()); ASSERT_FALSE(registry.valid(entities[0u])); ASSERT_FALSE(registry.valid(entities[1u])); ASSERT_TRUE(registry.valid(entities[2u])); ASSERT_EQ(registry.storage().size(), 1u); ASSERT_EQ(registry.storage().size(), 2u); ASSERT_EQ(registry.storage().size(), 0u); registry.destroy(iview.begin(), iview.end()); ASSERT_FALSE(registry.valid(entities[2u])); ASSERT_EQ(iview.size(), 0u); ASSERT_EQ(icview.size_hint(), 0u); ASSERT_EQ(registry.storage().size(), 0u); ASSERT_EQ(registry.storage().size(), 2u); ASSERT_EQ(registry.storage().size(), 0u); } TEST(Registry, ReleaseVersion) { entt::registry registry; entt::entity entities[2u]; registry.create(std::begin(entities), std::end(entities)); ASSERT_EQ(registry.current(entities[0u]), 0u); ASSERT_EQ(registry.current(entities[1u]), 0u); registry.release(entities[0u]); registry.release(entities[1u], 3); ASSERT_EQ(registry.current(entities[0u]), 1u); ASSERT_EQ(registry.current(entities[1u]), 3u); } ENTT_DEBUG_TEST(RegistryDeathTest, ReleaseVersion) { entt::registry registry; entt::entity entity = registry.create(); registry.release(entity); ASSERT_DEATH(registry.release(entity), ""); ASSERT_DEATH(registry.release(entity, 3), ""); } TEST(Registry, ReleaseRange) { entt::registry registry; entt::entity entities[3u]; registry.create(std::begin(entities), std::end(entities)); ASSERT_TRUE(registry.valid(entities[0u])); ASSERT_TRUE(registry.valid(entities[1u])); ASSERT_TRUE(registry.valid(entities[2u])); registry.release(std::begin(entities), std::end(entities) - 1u); ASSERT_FALSE(registry.valid(entities[0u])); ASSERT_FALSE(registry.valid(entities[1u])); ASSERT_TRUE(registry.valid(entities[2u])); registry.release(std::end(entities) - 1u, std::end(entities)); ASSERT_FALSE(registry.valid(entities[2u])); } TEST(Registry, VersionOverflow) { using traits_type = entt::entt_traits; entt::registry registry; const auto entity = registry.create(); registry.release(entity); ASSERT_NE(registry.current(entity), traits_type::to_version(entity)); ASSERT_NE(registry.current(entity), typename traits_type::version_type{}); registry.release(registry.create(), traits_type::to_version(entt::tombstone) - 1u); registry.release(registry.create()); ASSERT_EQ(registry.current(entity), traits_type::to_version(entity)); ASSERT_EQ(registry.current(entity), typename traits_type::version_type{}); } TEST(Registry, NullEntity) { entt::registry registry; const entt::entity entity = entt::null; ASSERT_FALSE(registry.valid(entity)); ASSERT_NE(registry.create(entity), entity); } TEST(Registry, TombstoneVersion) { using traits_type = entt::entt_traits; entt::registry registry; const entt::entity entity = entt::tombstone; ASSERT_FALSE(registry.valid(entity)); const auto other = registry.create(); const auto vers = traits_type::to_version(entity); const auto required = traits_type::construct(traits_type::to_entity(other), vers); ASSERT_NE(registry.release(other, vers), vers); ASSERT_NE(registry.create(required), required); } TEST(Registry, Each) { entt::registry registry; entt::registry::size_type tot; entt::registry::size_type match; static_cast(registry.create()); registry.emplace(registry.create()); static_cast(registry.create()); registry.emplace(registry.create()); static_cast(registry.create()); tot = 0u; match = 0u; registry.each([&](auto entity) { match += registry.all_of(entity); static_cast(registry.create()); ++tot; }); ASSERT_EQ(tot, 5u); ASSERT_EQ(match, 2u); tot = 0u; match = 0u; registry.each([&](auto entity) { if(registry.all_of(entity)) { registry.destroy(entity); ++match; } ++tot; }); ASSERT_EQ(tot, 10u); ASSERT_EQ(match, 2u); tot = 0u; match = 0u; registry.each([&](auto entity) { match += registry.all_of(entity); registry.destroy(entity); ++tot; }); ASSERT_EQ(tot, 8u); ASSERT_EQ(match, 0u); registry.each([&](auto) { FAIL(); }); } TEST(Registry, Orphans) { entt::registry registry; entt::entity entities[3u]{}; registry.create(std::begin(entities), std::end(entities)); registry.emplace(entities[0u]); registry.emplace(entities[2u]); registry.each([&](const auto entt) { ASSERT_TRUE(entt != entities[1u] || registry.orphan(entt)); }); registry.erase(entities[0u]); registry.erase(entities[2u]); registry.each([&](const auto entt) { ASSERT_TRUE(registry.orphan(entt)); }); } TEST(Registry, View) { entt::registry registry; entt::entity entities[3u]; auto iview = registry.view(); auto cview = registry.view(); auto mview = registry.view(); auto fview = registry.view(entt::exclude); registry.create(std::begin(entities), std::end(entities)); registry.emplace(entities[0u], 0); registry.emplace(entities[0u], 'c'); registry.emplace(entities[1u], 0); registry.emplace(entities[2u], 0); registry.emplace(entities[2u], 'c'); ASSERT_EQ(iview.size(), 3u); ASSERT_EQ(cview.size(), 2u); ASSERT_EQ(mview.size_hint(), 3u); ASSERT_EQ(fview.size_hint(), 3u); mview.refresh(); fview.refresh(); ASSERT_EQ(mview.size_hint(), 2u); ASSERT_EQ(fview.size_hint(), 3u); ASSERT_NE(mview.begin(), mview.end()); ASSERT_NE(fview.begin(), fview.end()); ASSERT_EQ(std::distance(mview.begin(), mview.end()), 2); ASSERT_EQ(std::distance(fview.begin(), fview.end()), 1); mview.each([&entities, first = true](auto entity, auto &&...) mutable { ASSERT_EQ(entity, entities[2u * first]); first = false; }); fview.each([&entities](auto entity, auto &&...) { ASSERT_EQ(entity, entities[1u]); }); } TEST(Registry, ExcludeOnlyView) { entt::registry registry; entt::entity entities[4u]; auto view = registry.view(entt::exclude); registry.create(std::begin(entities), std::end(entities)); registry.emplace(entities[0u], 0); registry.emplace(entities[2u], 0); registry.emplace(entities[3u], 0); registry.destroy(entities[3u]); ASSERT_EQ(view.size_hint(), 4u); ASSERT_NE(view.begin(), view.end()); // returns all matching identifiers, both in-use and available ones ASSERT_EQ(std::distance(view.begin(), view.end()), 2); // skips available identifiers automatically, only returns in-use elements view.each([&entities](auto entity, auto &&...) { ASSERT_EQ(entity, entities[1u]); }); } TEST(Registry, NonOwningGroupInitOnFirstUse) { entt::registry registry; entt::entity entities[3u]; registry.create(std::begin(entities), std::end(entities)); registry.insert(std::begin(entities), std::end(entities), 0); registry.emplace(entities[0u], 'c'); registry.emplace(entities[2u], 'c'); std::size_t cnt{}; auto group = registry.group(entt::get); group.each([&cnt](auto...) { ++cnt; }); ASSERT_FALSE((registry.owned())); ASSERT_EQ(cnt, 2u); } TEST(Registry, NonOwningGroupInitOnEmplace) { entt::registry registry; entt::entity entities[3u]; auto group = registry.group(entt::get); registry.create(std::begin(entities), std::end(entities)); registry.insert(std::begin(entities), std::end(entities), 0); registry.emplace(entities[0u], 'c'); registry.emplace(entities[2u], 'c'); std::size_t cnt{}; group.each([&cnt](auto...) { ++cnt; }); ASSERT_FALSE((registry.owned())); ASSERT_EQ(cnt, 2u); } TEST(Registry, FullOwningGroupInitOnFirstUse) { entt::registry registry; entt::entity entities[3u]; registry.create(std::begin(entities), std::end(entities)); registry.insert(std::begin(entities), std::end(entities), 0); registry.emplace(entities[0u], 'c'); registry.emplace(entities[2u], 'c'); std::size_t cnt{}; auto group = registry.group(); group.each([&cnt](auto...) { ++cnt; }); ASSERT_TRUE(registry.owned()); ASSERT_TRUE(registry.owned()); ASSERT_FALSE(registry.owned()); ASSERT_EQ(cnt, 2u); } TEST(Registry, FullOwningGroupInitOnEmplace) { entt::registry registry; entt::entity entities[3u]; auto group = registry.group(); registry.create(std::begin(entities), std::end(entities)); registry.insert(std::begin(entities), std::end(entities), 0); registry.emplace(entities[0u], 'c'); registry.emplace(entities[2u], 'c'); std::size_t cnt{}; group.each([&cnt](auto...) { ++cnt; }); ASSERT_TRUE(registry.owned()); ASSERT_TRUE(registry.owned()); ASSERT_FALSE(registry.owned()); ASSERT_EQ(cnt, 2u); } TEST(Registry, PartialOwningGroupInitOnFirstUse) { entt::registry registry; entt::entity entities[3u]; registry.create(std::begin(entities), std::end(entities)); registry.insert(std::begin(entities), std::end(entities), 0); registry.emplace(entities[0u], 'c'); registry.emplace(entities[2u], 'c'); std::size_t cnt{}; auto group = registry.group(entt::get); group.each([&cnt](auto...) { ++cnt; }); ASSERT_TRUE((registry.owned())); ASSERT_TRUE(registry.owned()); ASSERT_FALSE(registry.owned()); ASSERT_EQ(cnt, 2u); } TEST(Registry, PartialOwningGroupInitOnEmplace) { entt::registry registry; entt::entity entities[3u]; auto group = registry.group(entt::get); registry.create(std::begin(entities), std::end(entities)); registry.insert(std::begin(entities), std::end(entities), 0); registry.emplace(entities[0u], 'c'); registry.emplace(entities[2u], 'c'); std::size_t cnt{}; group.each([&cnt](auto...) { ++cnt; }); ASSERT_TRUE((registry.owned())); ASSERT_TRUE(registry.owned()); ASSERT_FALSE(registry.owned()); ASSERT_EQ(cnt, 2u); } TEST(Registry, CleanViewAfterRemoveAndClear) { entt::registry registry; auto view = registry.view(); const auto entity = registry.create(); registry.emplace(entity); registry.emplace(entity); ASSERT_EQ(view.size_hint(), 1u); registry.erase(entity); ASSERT_EQ(view.size_hint(), 1u); registry.emplace(entity); ASSERT_EQ(view.size_hint(), 1u); registry.clear(); ASSERT_EQ(view.size_hint(), 0u); registry.emplace(entity); ASSERT_EQ(view.size_hint(), 1u); registry.clear(); ASSERT_EQ(view.size_hint(), 0u); } TEST(Registry, CleanNonOwningGroupViewAfterRemoveAndClear) { entt::registry registry; auto group = registry.group(entt::get); const auto entity = registry.create(); registry.emplace(entity, 0); registry.emplace(entity, 'c'); ASSERT_EQ(group.size(), 1u); registry.erase(entity); ASSERT_EQ(group.size(), 0u); registry.emplace(entity, 'c'); ASSERT_EQ(group.size(), 1u); registry.clear(); ASSERT_EQ(group.size(), 0u); registry.emplace(entity, 0); ASSERT_EQ(group.size(), 1u); registry.clear(); ASSERT_EQ(group.size(), 0u); } TEST(Registry, CleanFullOwningGroupViewAfterRemoveAndClear) { entt::registry registry; auto group = registry.group(); const auto entity = registry.create(); registry.emplace(entity, 0); registry.emplace(entity, 'c'); ASSERT_EQ(group.size(), 1u); registry.erase(entity); ASSERT_EQ(group.size(), 0u); registry.emplace(entity, 'c'); ASSERT_EQ(group.size(), 1u); registry.clear(); ASSERT_EQ(group.size(), 0u); registry.emplace(entity, 0); ASSERT_EQ(group.size(), 1u); registry.clear(); ASSERT_EQ(group.size(), 0u); } TEST(Registry, CleanPartialOwningGroupViewAfterRemoveAndClear) { entt::registry registry; auto group = registry.group(entt::get); const auto entity = registry.create(); registry.emplace(entity, 0); registry.emplace(entity, 'c'); ASSERT_EQ(group.size(), 1u); registry.erase(entity); ASSERT_EQ(group.size(), 0u); registry.emplace(entity, 'c'); ASSERT_EQ(group.size(), 1u); registry.clear(); ASSERT_EQ(group.size(), 0u); registry.emplace(entity, 0); ASSERT_EQ(group.size(), 1u); registry.clear(); ASSERT_EQ(group.size(), 0u); } ENTT_DEBUG_TEST(RegistryDeathTest, NestedGroups) { entt::registry registry; registry.group(entt::get); ASSERT_DEATH(registry.group(entt::get), ""); ASSERT_DEATH(registry.group(entt::get), ""); ASSERT_DEATH(registry.group(entt::get, entt::exclude), ""); ASSERT_DEATH((registry.group()), ""); } ENTT_DEBUG_TEST(RegistryDeathTest, ConflictingGroups) { entt::registry registry; registry.group(entt::get, entt::exclude); ASSERT_DEATH(registry.group(entt::get, entt::exclude), ""); } TEST(Registry, SortSingle) { entt::registry registry; int val = 0; registry.emplace(registry.create(), val++); registry.emplace(registry.create(), val++); registry.emplace(registry.create(), val++); for(auto entity: registry.view()) { ASSERT_EQ(registry.get(entity), --val); } registry.sort(std::less{}); for(auto entity: registry.view()) { ASSERT_EQ(registry.get(entity), val++); } } TEST(Registry, SortMulti) { entt::registry registry; unsigned int uval = 0u; int ival = 0; for(auto i = 0; i < 3; ++i) { const auto entity = registry.create(); registry.emplace(entity, uval++); registry.emplace(entity, ival++); } for(auto entity: registry.view()) { ASSERT_EQ(registry.get(entity), --uval); } for(auto entity: registry.view()) { ASSERT_EQ(registry.get(entity), --ival); } registry.sort(std::less{}); registry.sort(); for(auto entity: registry.view()) { ASSERT_EQ(registry.get(entity), uval++); } for(auto entity: registry.view()) { ASSERT_EQ(registry.get(entity), ival++); } } TEST(Registry, SortEmpty) { entt::registry registry; registry.emplace(registry.create()); registry.emplace(registry.create()); registry.emplace(registry.create()); ASSERT_LT(registry.storage().data()[0], registry.storage().data()[1]); ASSERT_LT(registry.storage().data()[1], registry.storage().data()[2]); registry.sort(std::less{}); ASSERT_GT(registry.storage().data()[0], registry.storage().data()[1]); ASSERT_GT(registry.storage().data()[1], registry.storage().data()[2]); } TEST(Registry, ComponentsWithTypesFromStandardTemplateLibrary) { // see #37 - the test shouldn't crash, that's all entt::registry registry; const auto entity = registry.create(); registry.emplace>(entity).insert(42); registry.destroy(entity); } TEST(Registry, ConstructWithComponents) { // it should compile, that's all entt::registry registry; const auto value = 0; registry.emplace(registry.create(), value); } TEST(Registry, Signals) { entt::registry registry; entt::entity entities[2u]; listener listener; registry.on_construct().connect<&listener::incr>(listener); registry.on_destroy().connect<&listener::decr>(listener); registry.on_construct().connect<&listener::incr>(listener); registry.on_destroy().connect<&listener::decr>(listener); registry.create(std::begin(entities), std::end(entities)); registry.insert(std::begin(entities), std::end(entities)); ASSERT_EQ(listener.counter, 2); ASSERT_EQ(listener.last, entities[1u]); registry.insert(std::rbegin(entities), std::rend(entities)); ASSERT_EQ(listener.counter, 4); ASSERT_EQ(listener.last, entities[0u]); registry.erase(entities[0u]); ASSERT_EQ(listener.counter, 2); ASSERT_EQ(listener.last, entities[0u]); registry.on_destroy().disconnect<&listener::decr>(listener); registry.on_destroy().disconnect<&listener::decr>(listener); registry.erase(entities[1u]); ASSERT_EQ(listener.counter, 2); ASSERT_EQ(listener.last, entities[0u]); registry.on_construct().disconnect<&listener::incr>(listener); registry.on_construct().disconnect<&listener::incr>(listener); registry.emplace(entities[1u]); registry.emplace(entities[1u]); ASSERT_EQ(listener.counter, 2); ASSERT_EQ(listener.last, entities[0u]); registry.on_construct().connect<&listener::incr>(listener); registry.on_destroy().connect<&listener::decr>(listener); registry.emplace(entities[0u]); registry.erase(entities[1u]); ASSERT_EQ(listener.counter, 2); ASSERT_EQ(listener.last, entities[1u]); registry.on_construct().connect<&listener::incr>(listener); registry.on_destroy().connect<&listener::decr>(listener); registry.erase(entities[1u]); registry.emplace(entities[0u]); ASSERT_EQ(listener.counter, 2); ASSERT_EQ(listener.last, entities[0u]); registry.clear(); ASSERT_EQ(listener.counter, 0); ASSERT_EQ(listener.last, entities[0u]); registry.insert(std::begin(entities), std::end(entities)); registry.insert(std::begin(entities), std::end(entities)); registry.destroy(entities[1u]); ASSERT_EQ(listener.counter, 2); ASSERT_EQ(listener.last, entities[1u]); registry.erase(entities[0u]); registry.emplace_or_replace(entities[0u]); registry.emplace_or_replace(entities[0u]); ASSERT_EQ(listener.counter, 2); ASSERT_EQ(listener.last, entities[0u]); registry.on_destroy().disconnect<&listener::decr>(listener); registry.on_destroy().disconnect<&listener::decr>(listener); registry.emplace_or_replace(entities[0u]); registry.emplace_or_replace(entities[0u]); ASSERT_EQ(listener.counter, 2); ASSERT_EQ(listener.last, entities[0u]); registry.on_update().connect<&listener::incr>(listener); registry.on_update().connect<&listener::incr>(listener); registry.emplace_or_replace(entities[0u]); registry.emplace_or_replace(entities[0u]); ASSERT_EQ(listener.counter, 4); ASSERT_EQ(listener.last, entities[0u]); registry.replace(entities[0u]); registry.replace(entities[0u]); ASSERT_EQ(listener.counter, 6); ASSERT_EQ(listener.last, entities[0u]); } TEST(Registry, SignalsOnRuntimePool) { using namespace entt::literals; entt::registry registry; const auto entity = registry.create(); listener listener; registry.on_construct("custom"_hs).connect<&listener::incr>(listener); registry.on_update("custom"_hs).connect<&listener::incr>(listener); registry.on_destroy("custom"_hs).connect<&listener::incr>(listener); ASSERT_EQ(listener.counter, 0); registry.emplace(entity); registry.patch(entity); registry.erase(entity); ASSERT_EQ(listener.counter, 0); registry.storage("custom"_hs).emplace(entity); registry.storage("custom"_hs).patch(entity); registry.storage("custom"_hs).erase(entity); ASSERT_EQ(listener.counter, 3); } TEST(Registry, SignalsOnEntity) { entt::registry registry; listener listener; registry.on_construct().connect<&listener::incr>(listener); entt::entity entity = registry.create(); entt::entity other = registry.create(); ASSERT_EQ(listener.counter, 2); ASSERT_EQ(listener.last, other); registry.destroy(other); registry.destroy(entity); ASSERT_EQ(listener.counter, 2); ASSERT_EQ(listener.last, other); registry.on_construct().disconnect(&listener); other = registry.create(); entity = registry.create(); ASSERT_EQ(listener.counter, 2); ASSERT_NE(listener.last, entity); ASSERT_NE(listener.last, other); registry.on_update().connect<&listener::decr>(listener); registry.patch(entity); ASSERT_EQ(listener.counter, 1); ASSERT_EQ(listener.last, entity); registry.on_update().disconnect(&listener); registry.patch(other); ASSERT_EQ(listener.counter, 1); ASSERT_NE(listener.last, other); registry.on_destroy().connect<&listener::decr>(listener); registry.destroy(entity); ASSERT_EQ(listener.counter, 0); ASSERT_EQ(listener.last, entity); registry.on_destroy().disconnect(&listener); registry.destroy(other); ASSERT_EQ(listener.counter, 0); ASSERT_NE(listener.last, other); } TEST(Registry, SignalWhenDestroying) { entt::registry registry; const auto entity = registry.create(); registry.on_destroy().connect<&entt::registry::remove>(); registry.emplace(entity); registry.emplace(entity); ASSERT_NE(registry.storage(entt::type_id().hash()), nullptr); ASSERT_NE(registry.storage(entt::type_id().hash()), nullptr); ASSERT_EQ(registry.storage(entt::type_id().hash()), nullptr); ASSERT_TRUE(registry.valid(entity)); registry.destroy(entity); ASSERT_NE(registry.storage(entt::type_id().hash()), nullptr); ASSERT_FALSE(registry.valid(entity)); } TEST(Registry, Insert) { entt::registry registry; entt::entity entities[3u]; registry.create(std::begin(entities), std::end(entities)); registry.emplace(entities[0u]); registry.emplace(entities[0u]); registry.emplace(entities[0u]); registry.emplace(entities[1u]); registry.emplace(entities[1u]); registry.emplace(entities[2u]); ASSERT_FALSE(registry.all_of(entities[0u])); ASSERT_FALSE(registry.all_of(entities[1u])); ASSERT_FALSE(registry.all_of(entities[2u])); const auto icview = registry.view(); registry.insert(icview.begin(), icview.end(), 3.f); ASSERT_EQ(registry.get(entities[0u]), 3.f); ASSERT_EQ(registry.get(entities[1u]), 3.f); ASSERT_FALSE(registry.all_of(entities[2u])); registry.clear(); float value[3]{0.f, 1.f, 2.f}; const auto iview = registry.view(); registry.insert(iview.rbegin(), iview.rend(), value); ASSERT_EQ(registry.get(entities[0u]), 0.f); ASSERT_EQ(registry.get(entities[1u]), 1.f); ASSERT_EQ(registry.get(entities[2u]), 2.f); } TEST(Registry, Erase) { entt::registry registry; const auto iview = registry.view(); const auto icview = registry.view(); entt::entity entities[3u]; registry.create(std::begin(entities), std::end(entities)); registry.emplace(entities[0u]); registry.emplace(entities[0u]); registry.emplace(entities[0u]); registry.emplace(entities[1u]); registry.emplace(entities[1u]); registry.emplace(entities[2u]); ASSERT_TRUE(registry.any_of(entities[0u])); ASSERT_TRUE(registry.all_of(entities[1u])); ASSERT_TRUE(registry.any_of(entities[2u])); registry.erase(entities[0u]); registry.erase(icview.begin(), icview.end()); ASSERT_FALSE(registry.any_of(entities[0u])); ASSERT_FALSE(registry.all_of(entities[1u])); ASSERT_TRUE(registry.any_of(entities[2u])); ASSERT_EQ(registry.storage().size(), 1u); ASSERT_EQ(registry.storage().size(), 0u); ASSERT_EQ(registry.storage().size(), 1u); registry.erase(iview.begin(), iview.end()); ASSERT_FALSE(registry.any_of(entities[2u])); ASSERT_NO_FATAL_FAILURE(registry.erase(iview.rbegin(), iview.rend())); ASSERT_EQ(registry.storage().size(), 0u); ASSERT_EQ(registry.storage().size(), 0u); ASSERT_EQ(registry.storage().size(), 1u); registry.insert(std::begin(entities) + 1, std::end(entities) - 1u); registry.insert(std::begin(entities) + 1, std::end(entities) - 1u); ASSERT_EQ(registry.storage().size(), 1u); ASSERT_EQ(registry.storage().size(), 1u); registry.erase(iview.begin(), iview.end()); ASSERT_EQ(registry.storage().size(), 0u); ASSERT_EQ(registry.storage().size(), 0u); registry.insert(std::begin(entities), std::end(entities)); registry.insert(std::begin(entities), std::end(entities)); ASSERT_EQ(registry.storage().size(), 3u); ASSERT_EQ(registry.storage().size(), 3u); registry.erase(std::begin(entities), std::end(entities)); ASSERT_EQ(registry.storage().size(), 0u); ASSERT_EQ(registry.storage().size(), 0u); ASSERT_FALSE(registry.orphan(entities[0u])); ASSERT_TRUE(registry.orphan(entities[1u])); ASSERT_TRUE(registry.orphan(entities[2u])); } ENTT_DEBUG_TEST(RegistryDeathTest, Erase) { entt::registry registry; const entt::entity entities[1u]{registry.create()}; ASSERT_FALSE((registry.any_of(entities[0u]))); ASSERT_DEATH((registry.erase(std::begin(entities), std::end(entities))), ""); ASSERT_DEATH(registry.erase(entities[0u]), ""); } TEST(Registry, StableErase) { entt::registry registry; const auto iview = registry.view(); const auto icview = registry.view(); entt::entity entities[3u]; registry.create(std::begin(entities), std::end(entities)); registry.emplace(entities[0u]); registry.emplace(entities[0u]); registry.emplace(entities[0u]); registry.emplace(entities[1u]); registry.emplace(entities[1u]); registry.emplace(entities[2u]); ASSERT_TRUE(registry.any_of(entities[0u])); ASSERT_TRUE(registry.all_of(entities[1u])); ASSERT_TRUE(registry.any_of(entities[2u])); registry.erase(entities[0u]); registry.erase(icview.begin(), icview.end()); registry.erase(icview.begin(), icview.end()); ASSERT_FALSE(registry.any_of(entities[0u])); ASSERT_FALSE(registry.all_of(entities[1u])); ASSERT_TRUE(registry.any_of(entities[2u])); ASSERT_EQ(registry.storage().size(), 1u); ASSERT_EQ(registry.storage().size(), 2u); ASSERT_EQ(registry.storage().size(), 1u); registry.erase(iview.begin(), iview.end()); ASSERT_FALSE(registry.any_of(entities[2u])); ASSERT_EQ(registry.storage().size(), 0u); ASSERT_EQ(registry.storage().size(), 2u); ASSERT_EQ(registry.storage().size(), 1u); } TEST(Registry, EraseIf) { using namespace entt::literals; entt::registry registry; const auto entity = registry.create(); registry.emplace(entity); registry.storage("other"_hs).emplace(entity); registry.emplace(entity); ASSERT_TRUE(registry.storage().contains(entity)); ASSERT_TRUE(registry.storage("other"_hs).contains(entity)); ASSERT_TRUE(registry.storage().contains(entity)); registry.erase_if(entity, [](auto &&...) { return false; }); ASSERT_TRUE(registry.storage().contains(entity)); ASSERT_TRUE(registry.storage("other"_hs).contains(entity)); ASSERT_TRUE(registry.storage().contains(entity)); registry.erase_if(entity, [](entt::id_type id, auto &&...) { return id == "other"_hs; }); ASSERT_TRUE(registry.storage().contains(entity)); ASSERT_FALSE(registry.storage("other"_hs).contains(entity)); ASSERT_TRUE(registry.storage().contains(entity)); registry.erase_if(entity, [](auto, const auto &storage) { return storage.type() == entt::type_id(); }); ASSERT_TRUE(registry.storage().contains(entity)); ASSERT_FALSE(registry.storage("other"_hs).contains(entity)); ASSERT_FALSE(registry.storage().contains(entity)); } TEST(Registry, Remove) { entt::registry registry; const auto iview = registry.view(); const auto icview = registry.view(); entt::entity entities[3u]; registry.create(std::begin(entities), std::end(entities)); registry.emplace(entities[0u]); registry.emplace(entities[0u]); registry.emplace(entities[0u]); registry.emplace(entities[1u]); registry.emplace(entities[1u]); registry.emplace(entities[2u]); ASSERT_TRUE(registry.any_of(entities[0u])); ASSERT_TRUE(registry.all_of(entities[1u])); ASSERT_TRUE(registry.any_of(entities[2u])); registry.remove(entities[0u]); ASSERT_EQ((registry.remove(icview.begin(), icview.end())), 2u); ASSERT_EQ((registry.remove(icview.begin(), icview.end())), 0u); ASSERT_FALSE(registry.any_of(entities[0u])); ASSERT_FALSE(registry.all_of(entities[1u])); ASSERT_TRUE(registry.any_of(entities[2u])); ASSERT_EQ(registry.storage().size(), 1u); ASSERT_EQ(registry.storage().size(), 0u); ASSERT_EQ(registry.storage().size(), 1u); ASSERT_EQ((registry.remove(iview.begin(), iview.end())), 1u); ASSERT_EQ(registry.remove(entities[0u]), 0u); ASSERT_EQ(registry.remove(entities[1u]), 0u); ASSERT_FALSE(registry.any_of(entities[2u])); ASSERT_EQ(registry.remove(iview.begin(), iview.end()), 0u); ASSERT_EQ(registry.storage().size(), 0u); ASSERT_EQ(registry.storage().size(), 0u); ASSERT_EQ(registry.storage().size(), 1u); registry.insert(std::begin(entities) + 1, std::end(entities) - 1u); registry.insert(std::begin(entities) + 1, std::end(entities) - 1u); ASSERT_EQ(registry.storage().size(), 1u); ASSERT_EQ(registry.storage().size(), 1u); registry.remove(iview.begin(), iview.end()); registry.remove(iview.begin(), iview.end()); ASSERT_EQ(registry.storage().size(), 0u); ASSERT_EQ(registry.storage().size(), 0u); registry.insert(std::begin(entities), std::end(entities)); registry.insert(std::begin(entities), std::end(entities)); ASSERT_EQ(registry.storage().size(), 3u); ASSERT_EQ(registry.storage().size(), 3u); registry.remove(std::begin(entities), std::end(entities)); registry.remove(std::begin(entities), std::end(entities)); ASSERT_EQ(registry.storage().size(), 0u); ASSERT_EQ(registry.storage().size(), 0u); ASSERT_FALSE(registry.orphan(entities[0u])); ASSERT_TRUE(registry.orphan(entities[1u])); ASSERT_TRUE(registry.orphan(entities[2u])); } TEST(Registry, StableRemove) { entt::registry registry; const auto iview = registry.view(); const auto icview = registry.view(); entt::entity entities[3u]; registry.create(std::begin(entities), std::end(entities)); registry.emplace(entities[0u]); registry.emplace(entities[0u]); registry.emplace(entities[0u]); registry.emplace(entities[1u]); registry.emplace(entities[1u]); registry.emplace(entities[2u]); ASSERT_TRUE(registry.any_of(entities[0u])); ASSERT_TRUE(registry.all_of(entities[1u])); ASSERT_TRUE(registry.any_of(entities[2u])); registry.remove(entities[0u]); ASSERT_EQ((registry.remove(icview.begin(), icview.end())), 2u); ASSERT_EQ((registry.remove(icview.begin(), icview.end())), 0u); ASSERT_FALSE(registry.any_of(entities[0u])); ASSERT_FALSE(registry.all_of(entities[1u])); ASSERT_TRUE(registry.any_of(entities[2u])); ASSERT_EQ(registry.storage().size(), 1u); ASSERT_EQ(registry.storage().size(), 2u); ASSERT_EQ(registry.storage().size(), 1u); ASSERT_EQ((registry.remove(iview.begin(), iview.end())), 1u); ASSERT_EQ(registry.remove(entities[0u]), 0u); ASSERT_EQ(registry.remove(entities[1u]), 0u); ASSERT_FALSE(registry.any_of(entities[2u])); ASSERT_EQ(registry.storage().size(), 0u); ASSERT_EQ(registry.storage().size(), 2u); ASSERT_EQ(registry.storage().size(), 1u); } TEST(Registry, Compact) { entt::registry registry; entt::entity entities[2u]; registry.create(std::begin(entities), std::end(entities)); registry.emplace(entities[0u]); registry.emplace(entities[0u]); registry.emplace(entities[1u]); registry.emplace(entities[1u]); ASSERT_EQ(registry.storage().size(), 2u); ASSERT_EQ(registry.storage().size(), 2u); registry.destroy(std::begin(entities), std::end(entities)); ASSERT_EQ(registry.storage().size(), 0u); ASSERT_EQ(registry.storage().size(), 2u); registry.compact(); ASSERT_EQ(registry.storage().size(), 0u); ASSERT_EQ(registry.storage().size(), 2u); registry.compact(); ASSERT_EQ(registry.storage().size(), 0u); ASSERT_EQ(registry.storage().size(), 0u); } TEST(Registry, NonOwningGroupInterleaved) { entt::registry registry; typename entt::entity entity = entt::null; entity = registry.create(); registry.emplace(entity); registry.emplace(entity); const auto group = registry.group(entt::get); entity = registry.create(); registry.emplace(entity); registry.emplace(entity); std::size_t cnt{}; group.each([&cnt](auto...) { ++cnt; }); ASSERT_EQ(cnt, 2u); } TEST(Registry, FullOwningGroupInterleaved) { entt::registry registry; typename entt::entity entity = entt::null; entity = registry.create(); registry.emplace(entity); registry.emplace(entity); const auto group = registry.group(); entity = registry.create(); registry.emplace(entity); registry.emplace(entity); std::size_t cnt{}; group.each([&cnt](auto...) { ++cnt; }); ASSERT_EQ(cnt, 2u); } TEST(Registry, PartialOwningGroupInterleaved) { entt::registry registry; typename entt::entity entity = entt::null; entity = registry.create(); registry.emplace(entity); registry.emplace(entity); const auto group = registry.group(entt::get); entity = registry.create(); registry.emplace(entity); registry.emplace(entity); std::size_t cnt{}; group.each([&cnt](auto...) { ++cnt; }); ASSERT_EQ(cnt, 2u); } TEST(Registry, NonOwningGroupSortInterleaved) { entt::registry registry; const auto group = registry.group(entt::get); const auto e0 = registry.create(); registry.emplace(e0, 0); registry.emplace(e0, '0'); const auto e1 = registry.create(); registry.emplace(e1, 1); registry.emplace(e1, '1'); registry.sort(std::greater{}); registry.sort(std::less{}); const auto e2 = registry.create(); registry.emplace(e2, 2); registry.emplace(e2, '2'); group.each([e0, e1, e2](const auto entity, const auto &i, const auto &c) { if(entity == e0) { ASSERT_EQ(i, 0); ASSERT_EQ(c, '0'); } else if(entity == e1) { ASSERT_EQ(i, 1); ASSERT_EQ(c, '1'); } else if(entity == e2) { ASSERT_EQ(i, 2); ASSERT_EQ(c, '2'); } }); } TEST(Registry, GetOrEmplace) { entt::registry registry; const auto entity = registry.create(); const auto value = registry.get_or_emplace(entity, 3); // get_or_emplace must work for empty types static_cast(registry.get_or_emplace(entity)); ASSERT_TRUE((registry.all_of(entity))); ASSERT_EQ(registry.get(entity), value); ASSERT_EQ(registry.get(entity), 3); } TEST(Registry, Constness) { entt::registry registry; static_assert((std::is_same_v({})), int &>)); static_assert((std::is_same_v({})), void>)); static_assert((std::is_same_v({})), std::tuple<>>)); static_assert((std::is_same_v({})), int &>)); static_assert((std::is_same_v({})), std::tuple>)); static_assert((std::is_same_v({})), std::tuple<>>)); static_assert((std::is_same_v({})), int *>)); static_assert((std::is_same_v({})), std::tuple>)); static_assert((std::is_same_v()), int &>)); static_assert((std::is_same_v()), const char &>)); static_assert((std::is_same_v()), int *>)); static_assert((std::is_same_v()), const char *>)); static_assert((std::is_same_v({})), std::tuple<>>)); static_assert((std::is_same_v({})), const int &>)); static_assert((std::is_same_v({})), std::tuple>)); static_assert((std::is_same_v({})), std::tuple<>>)); static_assert((std::is_same_v({})), const int *>)); static_assert((std::is_same_v({})), std::tuple>)); static_assert((std::is_same_v()), const int &>)); static_assert((std::is_same_v()), const char &>)); static_assert((std::is_same_v()), const int *>)); static_assert((std::is_same_v()), const char *>)); } TEST(Registry, MoveOnlyComponent) { entt::registry registry; // the purpose is to ensure that move only types are always accepted registry.emplace>(registry.create()); } TEST(Registry, NonDefaultConstructibleComponent) { entt::registry registry; // the purpose is to ensure that non default constructible type are always accepted registry.emplace(registry.create(), 42); } TEST(Registry, Dependencies) { entt::registry registry; const auto entity = registry.create(); // required because of an issue of VS2019 constexpr auto emplace_or_replace = &entt::registry::emplace_or_replace; constexpr auto remove = &entt::registry::remove; registry.on_construct().connect(); registry.on_destroy().connect(); registry.emplace(entity, .3); ASSERT_FALSE(registry.all_of(entity)); ASSERT_EQ(registry.get(entity), .3); registry.emplace(entity); ASSERT_TRUE(registry.all_of(entity)); ASSERT_EQ(registry.get(entity), .0); registry.erase(entity); ASSERT_FALSE((registry.any_of(entity))); registry.on_construct().disconnect(); registry.on_destroy().disconnect(); registry.emplace(entity); ASSERT_TRUE((registry.any_of(entity))); ASSERT_FALSE(registry.all_of(entity)); } TEST(Registry, StableEmplace) { entt::registry registry; registry.on_construct().connect<&listener::sort>(); registry.emplace(registry.create(), 0); ASSERT_EQ(registry.emplace(registry.create(), 1), 1); } TEST(Registry, AssignEntities) { using traits_type = entt::entt_traits; entt::registry registry; entt::entity entities[3]; registry.create(std::begin(entities), std::end(entities)); registry.release(entities[1]); registry.release(entities[2]); entt::registry other; const auto *data = registry.data(); other.assign(data, data + registry.size(), registry.released()); ASSERT_EQ(registry.size(), other.size()); ASSERT_TRUE(other.valid(entities[0])); ASSERT_FALSE(other.valid(entities[1])); ASSERT_FALSE(other.valid(entities[2])); ASSERT_EQ(registry.create(), other.create()); ASSERT_EQ(traits_type::to_entity(other.create()), traits_type::to_integral(entities[1])); } TEST(Registry, ScramblingPoolsIsAllowed) { entt::registry registry; registry.on_destroy().connect<&listener::sort>(); for(std::size_t i{}; i < 2u; ++i) { const auto entity = registry.create(); registry.emplace(entity, static_cast(i)); } registry.destroy(registry.view().back()); // thanks to @andranik3949 for pointing out this missing test registry.view().each([](const auto entity, const auto &value) { ASSERT_EQ(static_cast(entt::to_integral(entity)), value); }); } TEST(Registry, RuntimePools) { using namespace entt::literals; entt::registry registry; auto &storage = registry.storage("other"_hs); const auto entity = registry.create(); static_assert(std::is_same_v()), entt::storage_type_t &>); static_assert(std::is_same_v()), const entt::storage_type_t *>); static_assert(std::is_same_v::base_type *>); static_assert(std::is_same_v::base_type *>); ASSERT_NE(registry.storage("other"_hs), nullptr); ASSERT_EQ(std::as_const(registry).storage("rehto"_hs), nullptr); ASSERT_EQ(®istry.storage("other"_hs), &storage); ASSERT_NE(std::as_const(registry).storage(), &storage); ASSERT_FALSE(registry.any_of(entity)); ASSERT_FALSE(storage.contains(entity)); registry.emplace(entity); ASSERT_FALSE(storage.contains(entity)); ASSERT_TRUE(registry.any_of(entity)); ASSERT_EQ((entt::basic_view{registry.storage(), storage}.size_hint()), 0u); storage.emplace(entity); ASSERT_TRUE(storage.contains(entity)); ASSERT_TRUE(registry.any_of(entity)); ASSERT_EQ((entt::basic_view{registry.storage(), storage}.size_hint()), 1u); registry.destroy(entity); ASSERT_EQ(registry.create(entity), entity); ASSERT_FALSE(storage.contains(entity)); ASSERT_FALSE(registry.any_of(entity)); } ENTT_DEBUG_TEST(RegistryDeathTest, RuntimePools) { using namespace entt::literals; entt::registry registry; registry.storage("other"_hs); ASSERT_DEATH(registry.storage("other"_hs), ""); ASSERT_DEATH(std::as_const(registry).storage("other"_hs), ""); } TEST(Registry, Storage) { using namespace entt::literals; entt::registry registry; const auto entity = registry.create(); auto &storage = registry.storage("int"_hs); storage.emplace(entity); for(auto [id, pool]: registry.storage()) { static_assert(std::is_same_v); static_assert(std::is_same_v); ASSERT_TRUE(pool.contains(entity)); ASSERT_EQ(std::addressof(storage), std::addressof(pool)); ASSERT_EQ(id, "int"_hs); } for(auto &&curr: std::as_const(registry).storage()) { static_assert(std::is_same_v); static_assert(std::is_same_v); ASSERT_TRUE(curr.second.contains(entity)); ASSERT_EQ(std::addressof(storage), std::addressof(curr.second)); ASSERT_EQ(curr.first, "int"_hs); } } TEST(Registry, RegistryStorageIterator) { entt::registry registry; const auto entity = registry.create(); registry.emplace(entity); auto test = [entity](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()); ASSERT_EQ(begin + 1, iterable.end()); ASSERT_EQ(end - 1, iterable.begin()); ASSERT_EQ(++begin, iterable.end()); ASSERT_EQ(--begin, iterable.begin()); ASSERT_EQ(begin += 1, iterable.end()); ASSERT_EQ(begin -= 1, iterable.begin()); ASSERT_EQ(begin + (end - begin), iterable.end()); ASSERT_EQ(begin - (begin - end), iterable.end()); ASSERT_EQ(end - (end - begin), iterable.begin()); ASSERT_EQ(end + (begin - end), iterable.begin()); ASSERT_EQ(begin[0u].first, iterable.begin()->first); ASSERT_EQ(std::addressof(begin[0u].second), std::addressof((*iterable.begin()).second)); ASSERT_LT(begin, end); ASSERT_LE(begin, iterable.begin()); ASSERT_GT(end, begin); ASSERT_GE(end, iterable.end()); ASSERT_EQ(begin[0u].first, entt::type_id().hash()); ASSERT_TRUE(begin[0u].second.contains(entity)); }; test(registry.storage()); test(std::as_const(registry).storage()); decltype(std::as_const(registry).storage().begin()) cit = registry.storage().begin(); ASSERT_EQ(cit, registry.storage().begin()); ASSERT_NE(cit, std::as_const(registry).storage().end()); } TEST(Registry, RegistryStorageIteratorConversion) { entt::registry registry; registry.storage(); auto proxy = registry.storage(); auto cproxy = std::as_const(registry).storage(); typename decltype(proxy)::iterator it = proxy.begin(); typename decltype(cproxy)::iterator cit = it; static_assert(std::is_same_v>); static_assert(std::is_same_v>); ASSERT_EQ(it->first, entt::type_id().hash()); ASSERT_EQ((*it).second.type(), entt::type_id()); ASSERT_EQ(it->first, cit->first); ASSERT_EQ((*it).second.type(), (*cit).second.type()); 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(Registry, VoidType) { using namespace entt::literals; entt::registry registry; const auto entity = registry.create(); auto &storage = registry.storage("custom"_hs); storage.emplace(entity); ASSERT_TRUE(registry.storage().empty()); ASSERT_FALSE(registry.storage("custom"_hs).empty()); ASSERT_TRUE(registry.storage("custom"_hs).contains(entity)); ASSERT_EQ(registry.storage().type(), entt::type_id()); ASSERT_EQ(registry.storage("custom"_hs).type(), entt::type_id()); } TEST(Registry, NoEtoType) { entt::registry registry; const auto entity = registry.create(); registry.emplace(entity); registry.emplace(entity, 42); ASSERT_NE(registry.storage().raw(), nullptr); ASSERT_NE(registry.try_get(entity), nullptr); ASSERT_EQ(registry.view().get(entity), std::as_const(registry).view().get(entity)); auto view = registry.view(); auto cview = std::as_const(registry).view(); ASSERT_EQ((std::get<0>(view.get(entity))), (std::get<0>(cview.get(entity)))); } TEST(Registry, CtxAndPoolMemberDestructionOrder) { auto registry = std::make_unique(); const auto entity = registry->create(); bool ctx_check = false; registry->ctx().emplace(); registry->emplace(entity, *registry, ctx_check); registry.reset(); ASSERT_TRUE(ctx_check); }