#include #include #include #include #include #include #include #include #include #include #include struct empty_type {}; struct stable_type { int value; }; template<> struct entt::component_traits: basic_component_traits { using in_place_delete = std::true_type; }; 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; }); } template void incr(const entt::registry &, entt::entity entity) { last = entity; ++counter; } template 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}; }; TEST(Registry, Context) { entt::registry registry; ASSERT_EQ(registry.try_ctx(), nullptr); ASSERT_EQ(registry.try_ctx(), nullptr); ASSERT_EQ(registry.try_ctx(), nullptr); registry.set(); registry.set(); // suppress the warning due to the [[nodiscard]] attribute static_cast(registry.ctx_or_set()); ASSERT_NE(registry.try_ctx(), nullptr); ASSERT_NE(registry.try_ctx(), nullptr); ASSERT_NE(registry.try_ctx(), nullptr); registry.unset(); registry.unset(); auto count = 0; registry.ctx([&count](auto info) { ASSERT_EQ(info.hash(), entt::type_hash::value()); ++count; }); ASSERT_EQ(count, 1); ASSERT_NE(registry.try_ctx(), nullptr); ASSERT_EQ(registry.try_ctx(), nullptr); ASSERT_EQ(registry.try_ctx(), nullptr); registry.set('c'); registry.set(0); registry.set(1.); registry.set(42); ASSERT_EQ(registry.ctx_or_set('a'), 'c'); ASSERT_NE(registry.try_ctx(), nullptr); ASSERT_EQ(registry.try_ctx(), ®istry.ctx()); ASSERT_EQ(registry.ctx(), std::as_const(registry).ctx()); ASSERT_EQ(registry.ctx(), 42); ASSERT_NE(registry.try_ctx(), nullptr); ASSERT_EQ(registry.try_ctx(), ®istry.ctx()); ASSERT_EQ(registry.ctx(), std::as_const(registry).ctx()); ASSERT_EQ(registry.ctx(), 1.); ASSERT_NE(registry.try_ctx(), nullptr); ASSERT_EQ(registry.try_ctx(), ®istry.ctx()); ASSERT_EQ(registry.ctx(), std::as_const(registry).ctx()); ASSERT_EQ(registry.try_ctx(), nullptr); } TEST(Registry, ContextAsRef) { entt::registry registry; int value{3}; registry.set(value); ASSERT_NE(registry.try_ctx(), nullptr); ASSERT_NE(registry.try_ctx(), nullptr); ASSERT_NE(std::as_const(registry).try_ctx(), nullptr); ASSERT_EQ(registry.ctx(), 3); ASSERT_EQ(registry.ctx(), 3); registry.ctx() = 42; ASSERT_EQ(registry.ctx(), 42); ASSERT_EQ(value, 42); value = 3; ASSERT_EQ(std::as_const(registry).ctx(), 3); } TEST(Registry, ContextAsConstRef) { entt::registry registry; int value{3}; registry.set(value); ASSERT_EQ(registry.try_ctx(), nullptr); ASSERT_NE(registry.try_ctx(), nullptr); ASSERT_NE(std::as_const(registry).try_ctx(), nullptr); ASSERT_EQ(registry.ctx(), 3); value = 42; ASSERT_EQ(std::as_const(registry).ctx(), 42); } TEST(Registry, Functionalities) { entt::registry registry; ASSERT_EQ(registry.size(), 0u); ASSERT_EQ(registry.alive(), 0u); ASSERT_NO_FATAL_FAILURE((registry.reserve(8))); ASSERT_NO_FATAL_FAILURE(registry.reserve_pools(16)); ASSERT_NO_FATAL_FAILURE(registry.reserve(42)); ASSERT_TRUE(registry.empty()); ASSERT_EQ(registry.capacity(), 42u); ASSERT_EQ(registry.capacity(), ENTT_PACKED_PAGE); ASSERT_EQ(registry.capacity(), ENTT_PACKED_PAGE); ASSERT_EQ(registry.size(), 0u); ASSERT_EQ(registry.size(), 0u); ASSERT_TRUE((registry.empty())); registry.prepare(); 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.size(), 1u); ASSERT_EQ(registry.size(), 1u); ASSERT_FALSE(registry.empty()); ASSERT_FALSE(registry.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(registry.version(e2), 0u); ASSERT_EQ(registry.current(e2), 0u); ASSERT_DEATH(registry.release(e2), ""); ASSERT_NO_FATAL_FAILURE(registry.destroy(e2)); ASSERT_DEATH(registry.destroy(e2), ""); ASSERT_EQ(registry.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.size(), 1u); ASSERT_EQ(registry.size(), 1u); ASSERT_FALSE(registry.empty()); ASSERT_FALSE(registry.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.size(), 0u); ASSERT_EQ(registry.size(), 1u); ASSERT_TRUE(registry.empty()); ASSERT_FALSE(registry.empty()); ASSERT_NO_FATAL_FAILURE(registry.clear()); ASSERT_EQ(registry.size(), 0u); ASSERT_EQ(registry.size(), 0u); ASSERT_TRUE((registry.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.size(), 0u); ASSERT_EQ(registry.size(), 0u); ASSERT_TRUE(registry.empty()); ASSERT_EQ(registry.capacity(), ENTT_PACKED_PAGE); ASSERT_EQ(registry.capacity(), ENTT_PACKED_PAGE); registry.shrink_to_fit(); ASSERT_EQ(registry.capacity(), 0u); ASSERT_EQ(registry.capacity(), 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.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, 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) { entt::registry registry; const auto pre = registry.create(); ASSERT_EQ(pre, registry.entity(pre)); registry.release(pre); const auto post = registry.create(); ASSERT_NE(pre, post); ASSERT_EQ(entt::registry::entity(pre), entt::registry::entity(post)); ASSERT_NE(entt::registry::version(pre), entt::registry::version(post)); ASSERT_NE(registry.version(pre), registry.current(pre)); ASSERT_EQ(registry.version(post), registry.current(post)); } TEST(Registry, Data) { 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_NE(*std::as_const(registry).data(), entity); ASSERT_EQ(*(std::as_const(registry).data() + 1u), other); } TEST(Registry, CreateManyEntitiesAtOnce) { 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(registry.entity(entities[0]), entt::entity{0}); ASSERT_EQ(registry.version(entities[0]), entt::registry::version_type{2}); ASSERT_EQ(registry.entity(entities[1]), entt::entity{1}); ASSERT_EQ(registry.version(entities[1]), entt::registry::version_type{1}); ASSERT_EQ(registry.entity(entities[2]), entt::entity{2}); ASSERT_EQ(registry.version(entities[2]), entt::registry::version_type{0}); } 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) { entt::registry registry; auto e3 = registry.create(entt::entity{3}); auto e2 = registry.create(entt::entity{3}); ASSERT_EQ(e2, entt::entity{2}); ASSERT_FALSE(registry.valid(entt::entity{1})); ASSERT_EQ(e3, entt::entity{3}); registry.release(e2); ASSERT_EQ(registry.version(e2), entt::registry::version_type{}); ASSERT_EQ(registry.current(e2), entt::registry::version_type{1}); e2 = registry.create(); auto e1 = registry.create(entt::entity{2}); ASSERT_EQ(registry.entity(e2), entt::entity{2}); ASSERT_EQ(registry.version(e2), entt::registry::version_type{1}); ASSERT_EQ(registry.entity(e1), entt::entity{1}); ASSERT_EQ(registry.version(e1), entt::registry::version_type{}); registry.release(e1); registry.release(e2); auto e0 = registry.create(entt::entity{0}); ASSERT_EQ(e0, entt::entity{0}); ASSERT_EQ(registry.version(e0), entt::registry::version_type{}); } TEST(Registry, CreateClearCycle) { 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(registry.version(pre), registry.version(post)); ASSERT_EQ(registry.version(pre) + 1, registry.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), entt::registry::version_type{1}); ASSERT_EQ(registry.current(e1), entt::registry::version_type{1}); } TEST(Registry, DestroyVersion) { entt::registry registry; const auto e0 = registry.create(); const auto e1 = registry.create(); ASSERT_EQ(registry.current(e0), entt::registry::version_type{}); ASSERT_EQ(registry.current(e1), entt::registry::version_type{}); registry.destroy(e0); registry.destroy(e1, 3); ASSERT_DEATH(registry.destroy(e0), ""); ASSERT_DEATH(registry.destroy(e1, 3), ""); ASSERT_EQ(registry.current(e0), entt::registry::version_type{1}); ASSERT_EQ(registry.current(e1), entt::registry::version_type{3}); } TEST(Registry, RangeDestroy) { 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()); registry.destroy(icview.rbegin(), icview.rend()); ASSERT_FALSE(registry.valid(entities[0u])); ASSERT_FALSE(registry.valid(entities[1u])); ASSERT_TRUE(registry.valid(entities[2u])); ASSERT_EQ(registry.size(), 1u); ASSERT_EQ(registry.size(), 0u); ASSERT_EQ(registry.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.size(), 0u); ASSERT_EQ(registry.size(), 0u); ASSERT_EQ(registry.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.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.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.size(), 1u); ASSERT_EQ(registry.size(), 2u); ASSERT_EQ(registry.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.size(), 0u); ASSERT_EQ(registry.size(), 2u); ASSERT_EQ(registry.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]), entt::registry::version_type{}); ASSERT_EQ(registry.current(entities[1u]), entt::registry::version_type{}); registry.release(entities[0u]); registry.release(entities[1u], 3); ASSERT_DEATH(registry.release(entities[0u]), ""); ASSERT_DEATH(registry.release(entities[1u], 3), ""); ASSERT_EQ(registry.current(entities[0u]), entt::registry::version_type{1}); ASSERT_EQ(registry.current(entities[1u]), entt::registry::version_type{3}); } TEST(Registry, RangeRelease) { 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), registry.version(entity)); ASSERT_NE(registry.current(entity), typename traits_type::version_type{}); registry.release(registry.create(), traits_type::to_version(traits_type::construct()) - 1u); registry.release(registry.create()); ASSERT_EQ(registry.current(entity), registry.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) { if(registry.all_of(entity)) { ++match; } 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) { if(registry.all_of(entity)) { ++match; } registry.destroy(entity); ++tot; }); ASSERT_EQ(tot, 8u); ASSERT_EQ(match, 0u); registry.each([&](auto) { FAIL(); }); } TEST(Registry, Orphans) { entt::registry registry; entt::registry::size_type tot{}; entt::entity entities[3u]{}; registry.create(std::begin(entities), std::end(entities)); registry.emplace(entities[0u]); registry.emplace(entities[2u]); registry.orphans([&](auto) { ++tot; }); ASSERT_EQ(tot, 1u); registry.erase(entities[0u]); registry.erase(entities[2u]); tot = {}; registry.orphans([&](auto) { ++tot; }); ASSERT_EQ(tot, 3u); registry.clear(); tot = {}; registry.orphans([&](auto) { ++tot; }); ASSERT_EQ(tot, 0u); } TEST(Registry, View) { entt::registry registry; auto mview = registry.view(); auto iview = registry.view(); auto cview = registry.view(); entt::entity entities[3u]; 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); std::size_t cnt{}; mview.each([&cnt](auto...) { ++cnt; }); ASSERT_EQ(cnt, 2u); } TEST(Registry, NonOwningGroupInitOnFirstUse) { entt::registry registry; auto create = [&](auto... component) { const auto entity = registry.create(); (registry.emplace(entity, component), ...); }; create(0, 'c'); create(0); create(0, 'c'); std::size_t cnt{}; auto group = registry.group<>(entt::get); group.each([&cnt](auto...) { ++cnt; }); ASSERT_TRUE((registry.sortable())); ASSERT_EQ(cnt, 2u); } TEST(Registry, NonOwningGroupInitOnEmplace) { entt::registry registry; auto group = registry.group<>(entt::get); auto create = [&](auto... component) { const auto entity = registry.create(); (registry.emplace(entity, component), ...); }; create(0, 'c'); create(0); create(0, 'c'); std::size_t cnt{}; group.each([&cnt](auto...) { ++cnt; }); ASSERT_TRUE((registry.sortable())); ASSERT_EQ(cnt, 2u); } TEST(Registry, FullOwningGroupInitOnFirstUse) { entt::registry registry; auto create = [&](auto... component) { const auto entity = registry.create(); (registry.emplace(entity, component), ...); }; create(0, 'c'); create(0); create(0, 'c'); std::size_t cnt{}; auto group = registry.group(); group.each([&cnt](auto...) { ++cnt; }); ASSERT_FALSE(registry.sortable()); ASSERT_FALSE(registry.sortable()); ASSERT_TRUE(registry.sortable()); ASSERT_EQ(cnt, 2u); } TEST(Registry, FullOwningGroupInitOnEmplace) { entt::registry registry; auto group = registry.group(); auto create = [&](auto... component) { const auto entity = registry.create(); (registry.emplace(entity, component), ...); }; create(0, 'c'); create(0); create(0, 'c'); std::size_t cnt{}; group.each([&cnt](auto...) { ++cnt; }); ASSERT_FALSE(registry.sortable()); ASSERT_FALSE(registry.sortable()); ASSERT_TRUE(registry.sortable()); ASSERT_EQ(cnt, 2u); } TEST(Registry, PartialOwningGroupInitOnFirstUse) { entt::registry registry; auto create = [&](auto... component) { const auto entity = registry.create(); (registry.emplace(entity, component), ...); }; create(0, 'c'); create(0); create(0, 'c'); std::size_t cnt{}; auto group = registry.group(entt::get); group.each([&cnt](auto...) { ++cnt; }); ASSERT_FALSE((registry.sortable())); ASSERT_FALSE(registry.sortable()); ASSERT_TRUE(registry.sortable()); ASSERT_EQ(cnt, 2u); } TEST(Registry, PartialOwningGroupInitOnEmplace) { entt::registry registry; auto group = registry.group(entt::get); auto create = [&](auto... component) { const auto entity = registry.create(); (registry.emplace(entity, component), ...); }; create(0, 'c'); create(0); create(0, 'c'); std::size_t cnt{}; group.each([&cnt](auto...) { ++cnt; }); ASSERT_FALSE((registry.sortable())); ASSERT_FALSE(registry.sortable()); ASSERT_TRUE(registry.sortable()); 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); } TEST(Registry, NestedGroups) { entt::registry registry; entt::entity entities[10]; registry.create(std::begin(entities), std::end(entities)); registry.insert(std::begin(entities), std::end(entities)); registry.insert(std::begin(entities), std::end(entities)); const auto g1 = registry.group(entt::get, entt::exclude); ASSERT_TRUE(registry.sortable(g1)); ASSERT_EQ(g1.size(), 10u); const auto g2 = registry.group(entt::get); ASSERT_TRUE(registry.sortable(g1)); ASSERT_FALSE(registry.sortable(g2)); ASSERT_EQ(g1.size(), 10u); ASSERT_EQ(g2.size(), 10u); for(auto i = 0u; i < 5u; ++i) { ASSERT_TRUE(g1.contains(entities[i*2+1])); ASSERT_TRUE(g1.contains(entities[i*2])); ASSERT_TRUE(g2.contains(entities[i*2+1])); ASSERT_TRUE(g2.contains(entities[i*2])); registry.emplace(entities[i*2]); } ASSERT_EQ(g1.size(), 5u); ASSERT_EQ(g2.size(), 10u); for(auto i = 0u; i < 5u; ++i) { ASSERT_TRUE(g1.contains(entities[i*2+1])); ASSERT_FALSE(g1.contains(entities[i*2])); ASSERT_TRUE(g2.contains(entities[i*2+1])); ASSERT_TRUE(g2.contains(entities[i*2])); registry.erase(entities[i*2+1]); } ASSERT_EQ(g1.size(), 0u); ASSERT_EQ(g2.size(), 5u); const auto g3= registry.group(entt::get, entt::exclude); ASSERT_FALSE(registry.sortable(g1)); ASSERT_FALSE(registry.sortable(g2)); ASSERT_TRUE(registry.sortable(g3)); ASSERT_EQ(g1.size(), 0u); ASSERT_EQ(g2.size(), 5u); ASSERT_EQ(g3.size(), 0u); for(auto i = 0u; i < 5u; ++i) { ASSERT_FALSE(g1.contains(entities[i*2+1])); ASSERT_FALSE(g1.contains(entities[i*2])); ASSERT_FALSE(g2.contains(entities[i*2+1])); ASSERT_TRUE(g2.contains(entities[i*2])); ASSERT_FALSE(g3.contains(entities[i*2+1])); ASSERT_FALSE(g3.contains(entities[i*2])); registry.emplace(entities[i*2+1]); } ASSERT_EQ(g1.size(), 5u); ASSERT_EQ(g2.size(), 10u); ASSERT_EQ(g3.size(), 0u); for(auto i = 0u; i < 5u; ++i) { ASSERT_TRUE(g1.contains(entities[i*2+1])); ASSERT_FALSE(g1.contains(entities[i*2])); ASSERT_TRUE(g2.contains(entities[i*2+1])); ASSERT_TRUE(g2.contains(entities[i*2])); ASSERT_FALSE(g3.contains(entities[i*2+1])); ASSERT_FALSE(g3.contains(entities[i*2])); registry.emplace(entities[i*2]); } ASSERT_EQ(g1.size(), 5u); ASSERT_EQ(g2.size(), 10u); ASSERT_EQ(g3.size(), 0u); for(auto i = 0u; i < 5u; ++i) { registry.erase(entities[i*2]); } ASSERT_EQ(g1.size(), 10u); ASSERT_EQ(g2.size(), 10u); ASSERT_EQ(g3.size(), 5u); for(auto i = 0u; i < 5u; ++i) { ASSERT_TRUE(g1.contains(entities[i*2+1])); ASSERT_TRUE(g1.contains(entities[i*2])); ASSERT_TRUE(g2.contains(entities[i*2+1])); ASSERT_TRUE(g2.contains(entities[i*2])); ASSERT_FALSE(g3.contains(entities[i*2+1])); ASSERT_TRUE(g3.contains(entities[i*2])); registry.erase(entities[i*2+1]); registry.erase(entities[i*2]); } ASSERT_EQ(g1.size(), 0u); ASSERT_EQ(g2.size(), 0u); ASSERT_EQ(g3.size(), 0u); } 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.view().data()[0], registry.view().data()[1]); ASSERT_LT(registry.view().data()[1], registry.view().data()[2]); registry.sort(std::less{}); ASSERT_GT(registry.view().data()[0], registry.view().data()[1]); ASSERT_GT(registry.view().data()[1], registry.view().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, 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.data(), iview.data() + iview.size(), 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()); registry.erase(icview.rbegin(), icview.rend()); ASSERT_FALSE(registry.any_of(entities[0u])); ASSERT_FALSE(registry.all_of(entities[1u])); ASSERT_TRUE(registry.any_of(entities[2u])); ASSERT_EQ(registry.size(), 1u); ASSERT_EQ(registry.size(), 0u); ASSERT_EQ(registry.size(), 1u); registry.erase(iview.begin(), iview.end()); ASSERT_DEATH(registry.erase(entities[0u]), ""); ASSERT_DEATH(registry.erase(entities[1u]), ""); ASSERT_FALSE(registry.any_of(entities[2u])); ASSERT_NO_FATAL_FAILURE(registry.erase(iview.rbegin(), iview.rend())); ASSERT_EQ(registry.size(), 0u); ASSERT_EQ(registry.size(), 0u); ASSERT_EQ(registry.size(), 1u); registry.insert(std::begin(entities), std::end(entities)); registry.insert(std::begin(entities), std::end(entities)); ASSERT_EQ(registry.size(), 3u); ASSERT_EQ(registry.size(), 3u); registry.erase(std::begin(entities), std::end(entities)); ASSERT_DEATH((registry.erase(std::begin(entities), std::end(entities))), ""); ASSERT_EQ(registry.size(), 0u); ASSERT_EQ(registry.size(), 0u); ASSERT_FALSE(registry.orphan(entities[0u])); ASSERT_TRUE(registry.orphan(entities[1u])); ASSERT_TRUE(registry.orphan(entities[2u])); } 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.size(), 1u); ASSERT_EQ(registry.size(), 2u); ASSERT_EQ(registry.size(), 1u); registry.erase(iview.begin(), iview.end()); ASSERT_DEATH(registry.erase(entities[0u]), ""); ASSERT_DEATH(registry.erase(entities[1u]), ""); ASSERT_FALSE(registry.any_of(entities[2u])); ASSERT_EQ(registry.size(), 0u); ASSERT_EQ(registry.size(), 2u); ASSERT_EQ(registry.size(), 1u); } 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.rbegin(), icview.rend())), 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.size(), 1u); ASSERT_EQ(registry.size(), 0u); ASSERT_EQ(registry.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.size(), 0u); ASSERT_EQ(registry.size(), 0u); ASSERT_EQ(registry.size(), 1u); registry.insert(std::begin(entities), std::end(entities)); registry.insert(std::begin(entities), std::end(entities)); ASSERT_EQ(registry.size(), 3u); ASSERT_EQ(registry.size(), 3u); registry.remove(std::begin(entities), std::end(entities)); registry.remove(std::begin(entities), std::end(entities)); ASSERT_EQ(registry.size(), 0u); ASSERT_EQ(registry.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.rbegin(), icview.rend())), 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.size(), 1u); ASSERT_EQ(registry.size(), 2u); ASSERT_EQ(registry.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.size(), 0u); ASSERT_EQ(registry.size(), 2u); ASSERT_EQ(registry.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.size(), 2u); ASSERT_EQ(registry.size(), 2u); registry.destroy(std::begin(entities), std::end(entities)); ASSERT_EQ(registry.size(), 0u); ASSERT_EQ(registry.size(), 2u); registry.compact(); ASSERT_EQ(registry.size(), 0u); ASSERT_EQ(registry.size(), 2u); registry.compact(); ASSERT_EQ(registry.size(), 0u); ASSERT_EQ(registry.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); 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({})), int &>)); 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({})), const int &>)); 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) { 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(other.entity(other.create()), entities[1]); } TEST(Registry, Visit) { entt::registry registry; const auto entity = registry.create(); const auto other = registry.create(); registry.emplace(entity); registry.emplace(other); registry.emplace(entity); bool hasType[3]{}; registry.visit([&hasType](auto info) { hasType[0] = hasType[0] || (info.hash() == entt::type_hash::value()); hasType[1] = hasType[1] || (info.hash() == entt::type_hash::value()); hasType[2] = hasType[2] || (info.hash() == entt::type_hash::value()); }); ASSERT_TRUE(hasType[0] && hasType[1] && hasType[2]); hasType[0] = hasType[1] = hasType[2] = false; registry.visit(entity, [&hasType](auto info) { hasType[0] = hasType[0] || (info.hash() == entt::type_hash::value()); hasType[1] = hasType[1] || (info.hash() == entt::type_hash::value()); hasType[2] = hasType[2] || (info.hash() == entt::type_hash::value()); }); ASSERT_TRUE(hasType[0] && !hasType[1] && hasType[2]); hasType[0] = hasType[2] = false; registry.visit(other, [&hasType](auto info) { hasType[0] = hasType[0] || (info.hash() == entt::type_hash::value()); hasType[1] = hasType[1] || (info.hash() == entt::type_hash::value()); hasType[2] = hasType[2] || (info.hash() == entt::type_hash::value()); }); ASSERT_TRUE(!hasType[0] && hasType[1] && !hasType[2]); hasType[1] = false; } 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(entt::to_integral(entity), value); }); }