#include #include #include #include #include #include #include #include struct listener { template void incr(entt::registry<> ®istry, entt::registry<>::entity_type entity) { ASSERT_TRUE(registry.valid(entity)); ASSERT_TRUE(registry.has(entity)); last = entity; ++counter; } template void decr(entt::registry<> ®istry, entt::registry<>::entity_type entity) { ASSERT_TRUE(registry.valid(entity)); ASSERT_TRUE(registry.has(entity)); last = entity; --counter; } entt::registry<>::entity_type last; int counter{0}; }; TEST(Registry, Types) { entt::registry<> registry; ASSERT_EQ(registry.type(), registry.type()); ASSERT_NE(registry.type(), registry.type()); } TEST(Registry, Functionalities) { entt::registry<> registry; ASSERT_EQ(registry.size(), entt::registry<>::size_type{0}); ASSERT_EQ(registry.alive(), entt::registry<>::size_type{0}); ASSERT_NO_THROW(registry.reserve(42)); ASSERT_NO_THROW(registry.reserve(8)); ASSERT_NO_THROW(registry.reserve(8)); ASSERT_TRUE(registry.empty()); ASSERT_EQ(registry.capacity(), entt::registry<>::size_type{42}); ASSERT_EQ(registry.capacity(), entt::registry<>::size_type{8}); ASSERT_EQ(registry.capacity(), entt::registry<>::size_type{8}); ASSERT_EQ(registry.size(), entt::registry<>::size_type{0}); ASSERT_EQ(registry.size(), entt::registry<>::size_type{0}); ASSERT_TRUE(registry.empty()); ASSERT_TRUE(registry.empty()); const auto e0 = registry.create(); const auto e1 = registry.create(); registry.assign(e1); registry.assign(e1); ASSERT_TRUE(registry.has<>(e0)); ASSERT_TRUE(registry.has<>(e1)); ASSERT_EQ(registry.size(), entt::registry<>::size_type{1}); ASSERT_EQ(registry.size(), entt::registry<>::size_type{1}); ASSERT_FALSE(registry.empty()); ASSERT_FALSE(registry.empty()); ASSERT_NE(e0, e1); ASSERT_FALSE(registry.has(e0)); ASSERT_TRUE(registry.has(e1)); ASSERT_FALSE(registry.has(e0)); ASSERT_TRUE(registry.has(e1)); ASSERT_FALSE((registry.has(e0))); ASSERT_TRUE((registry.has(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.assign(e0, 42), 42); ASSERT_EQ(registry.assign(e0, 'c'), 'c'); ASSERT_NO_THROW(registry.remove(e1)); ASSERT_NO_THROW(registry.remove(e1)); ASSERT_TRUE(registry.has(e0)); ASSERT_FALSE(registry.has(e1)); ASSERT_TRUE(registry.has(e0)); ASSERT_FALSE(registry.has(e1)); ASSERT_TRUE((registry.has(e0))); ASSERT_FALSE((registry.has(e1))); const auto e2 = registry.create(); registry.assign_or_replace(e2, registry.get(e0)); registry.assign_or_replace(e2, registry.get(e0)); ASSERT_TRUE(registry.has(e2)); ASSERT_TRUE(registry.has(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_NO_THROW(registry.replace(e0, 0)); ASSERT_EQ(registry.get(e0), 0); ASSERT_NO_THROW(registry.assign_or_replace(e0, 1)); ASSERT_NO_THROW(registry.assign_or_replace(e1, 1)); ASSERT_EQ(static_cast &>(registry).get(e0), 1); ASSERT_EQ(static_cast &>(registry).get(e1), 1); ASSERT_EQ(registry.size(), entt::registry<>::size_type{3}); ASSERT_EQ(registry.alive(), entt::registry<>::size_type{3}); ASSERT_FALSE(registry.empty()); ASSERT_EQ(registry.version(e2), entt::registry<>::version_type{0}); ASSERT_EQ(registry.current(e2), entt::registry<>::version_type{0}); ASSERT_NO_THROW(registry.destroy(e2)); ASSERT_EQ(registry.version(e2), entt::registry<>::version_type{0}); ASSERT_EQ(registry.current(e2), entt::registry<>::version_type{1}); ASSERT_TRUE(registry.valid(e0)); ASSERT_TRUE(registry.fast(e0)); ASSERT_TRUE(registry.valid(e1)); ASSERT_TRUE(registry.fast(e1)); ASSERT_FALSE(registry.valid(e2)); ASSERT_FALSE(registry.fast(e2)); ASSERT_EQ(registry.size(), entt::registry<>::size_type{3}); ASSERT_EQ(registry.alive(), entt::registry<>::size_type{2}); ASSERT_FALSE(registry.empty()); ASSERT_NO_THROW(registry.reset()); ASSERT_EQ(registry.size(), entt::registry<>::size_type{3}); ASSERT_EQ(registry.alive(), entt::registry<>::size_type{0}); ASSERT_TRUE(registry.empty()); const auto e3 = registry.create(); ASSERT_EQ(registry.get(e3, 3), 3); ASSERT_EQ(registry.get(e3, 'c'), 'c'); ASSERT_EQ(registry.size(), entt::registry<>::size_type{1}); ASSERT_EQ(registry.size(), entt::registry<>::size_type{1}); ASSERT_FALSE(registry.empty()); ASSERT_FALSE(registry.empty()); ASSERT_TRUE(registry.has(e3)); ASSERT_TRUE(registry.has(e3)); ASSERT_EQ(registry.get(e3), 3); ASSERT_EQ(registry.get(e3), 'c'); ASSERT_NO_THROW(registry.reset()); ASSERT_EQ(registry.size(), entt::registry<>::size_type{0}); ASSERT_EQ(registry.size(), entt::registry<>::size_type{1}); ASSERT_TRUE(registry.empty()); ASSERT_FALSE(registry.empty()); ASSERT_NO_THROW(registry.reset()); ASSERT_EQ(registry.size(), entt::registry<>::size_type{0}); ASSERT_EQ(registry.size(), entt::registry<>::size_type{0}); ASSERT_TRUE(registry.empty()); ASSERT_TRUE(registry.empty()); const auto e4 = registry.create(); const auto e5 = registry.create(); registry.assign(e4); ASSERT_NO_THROW(registry.reset(e4)); ASSERT_NO_THROW(registry.reset(e5)); ASSERT_EQ(registry.size(), entt::registry<>::size_type{0}); ASSERT_EQ(registry.size(), entt::registry<>::size_type{0}); ASSERT_TRUE(registry.empty()); } TEST(Registry, Identifiers) { entt::registry<> registry; const auto pre = registry.create(); ASSERT_EQ(pre, registry.entity(pre)); registry.destroy(pre); const auto post = registry.create(); ASSERT_NE(pre, post); ASSERT_EQ(registry.entity(pre), registry.entity(post)); ASSERT_NE(registry.version(pre), registry.version(post)); ASSERT_NE(registry.version(pre), registry.current(pre)); ASSERT_EQ(registry.version(post), registry.current(post)); } TEST(Registry, RawData) { entt::registry<> registry; const entt::registry<> &cregistry = registry; const auto entity = registry.create(); ASSERT_EQ(registry.raw(), nullptr); ASSERT_EQ(cregistry.raw(), nullptr); ASSERT_EQ(cregistry.data(), nullptr); registry.assign(entity, 42); ASSERT_NE(registry.raw(), nullptr); ASSERT_NE(cregistry.raw(), nullptr); ASSERT_NE(cregistry.data(), nullptr); ASSERT_EQ(*registry.raw(), 42); ASSERT_EQ(*cregistry.raw(), 42); ASSERT_EQ(*cregistry.data(), entity); } TEST(Registry, CreateDestroyCornerCase) { entt::registry<> registry; const auto e0 = registry.create(); const auto e1 = registry.create(); registry.destroy(e0); registry.destroy(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, VersionOverflow) { entt::registry<> registry; const auto entity = registry.create(); registry.destroy(entity); ASSERT_EQ(registry.version(entity), entt::registry<>::version_type{}); for(auto i = entt::entt_traits::entity_type>::version_mask; i; --i) { ASSERT_NE(registry.current(entity), registry.version(entity)); registry.destroy(registry.create()); } ASSERT_EQ(registry.current(entity), registry.version(entity)); } TEST(Registry, Each) { entt::registry<> registry; entt::registry<>::size_type tot; entt::registry<>::size_type match; registry.create(); registry.assign(registry.create()); registry.create(); registry.assign(registry.create()); registry.create(); tot = 0u; match = 0u; registry.each([&](auto entity) { if(registry.has(entity)) { ++match; } registry.create(); ++tot; }); ASSERT_EQ(tot, 5u); ASSERT_EQ(match, 2u); tot = 0u; match = 0u; registry.each([&](auto entity) { if(registry.has(entity)) { registry.destroy(entity); ++match; } ++tot; }); ASSERT_EQ(tot, 10u); ASSERT_EQ(match, 2u); tot = 0u; match = 0u; registry.each([&](auto entity) { if(registry.has(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{}; registry.assign(registry.create()); registry.create(); registry.assign(registry.create()); registry.orphans([&](auto) { ++tot; }); ASSERT_EQ(tot, 1u); tot = {}; registry.each([&](auto entity) { registry.reset(entity); }); registry.orphans([&](auto) { ++tot; }); ASSERT_EQ(tot, 3u); registry.reset(); tot = {}; registry.orphans([&](auto) { ++tot; }); ASSERT_EQ(tot, 0u); } TEST(Registry, CreateDestroyEntities) { entt::registry<> registry; entt::registry<>::entity_type pre{}, post{}; for(int i = 0; i < 10; ++i) { const auto entity = registry.create(); registry.assign(entity); } registry.reset(); for(int i = 0; i < 7; ++i) { const auto entity = registry.create(); registry.assign(entity); if(i == 3) { pre = entity; } } registry.reset(); 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, StandardView) { entt::registry<> registry; auto mview = registry.view(); auto iview = registry.view(); auto cview = registry.view(); const auto e0 = registry.create(); registry.assign(e0, 0); registry.assign(e0, 'c'); const auto e1 = registry.create(); registry.assign(e1, 0); const auto e2 = registry.create(); registry.assign(e2, 0); registry.assign(e2, 'c'); ASSERT_EQ(iview.size(), decltype(iview)::size_type{3}); ASSERT_EQ(cview.size(), decltype(cview)::size_type{2}); decltype(mview)::size_type cnt{0}; mview.each([&cnt](auto...) { ++cnt; }); ASSERT_EQ(cnt, decltype(mview)::size_type{2}); } TEST(Registry, PersistentView) { entt::registry<> registry; const auto e0 = registry.create(); registry.assign(e0, 0); registry.assign(e0, 'c'); const auto e1 = registry.create(); registry.assign(e1, 0); const auto e2 = registry.create(); registry.assign(e2, 0); registry.assign(e2, 'c'); auto view = registry.persistent_view(); decltype(view)::size_type cnt{0}; view.each([&cnt](auto...) { ++cnt; }); ASSERT_EQ(cnt, decltype(view)::size_type{2}); } TEST(Registry, RawView) { entt::registry<> registry; auto view = registry.raw_view(); const auto e0 = registry.create(); registry.assign(e0, 0); registry.assign(e0, 'c'); const auto e1 = registry.create(); registry.assign(e1, 0); registry.assign(e1, 'c'); decltype(view)::size_type cnt{0}; view.each([&cnt](auto &...) { ++cnt; }); ASSERT_EQ(cnt, decltype(view)::size_type{2}); } TEST(Registry, CleanStandardViewAfterReset) { entt::registry<> registry; auto view = registry.view(); registry.assign(registry.create(), 0); ASSERT_EQ(view.size(), entt::registry<>::size_type{1}); registry.reset(); ASSERT_EQ(view.size(), entt::registry<>::size_type{0}); } TEST(Registry, CleanPersistentViewAfterReset) { entt::registry<> registry; auto view = registry.persistent_view(); const auto entity = registry.create(); registry.assign(entity, 0); registry.assign(entity, 'c'); ASSERT_EQ(view.size(), entt::registry<>::size_type{1}); registry.reset(); ASSERT_EQ(view.size(), entt::registry<>::size_type{0}); } TEST(Registry, CleanRawViewAfterReset) { entt::registry<> registry; auto view = registry.raw_view(); registry.assign(registry.create(), 0); ASSERT_EQ(view.size(), entt::registry<>::size_type{1}); registry.reset(); ASSERT_EQ(view.size(), entt::registry<>::size_type{0}); } TEST(Registry, SortSingle) { entt::registry<> registry; int val = 0; registry.assign(registry.create(), val++); registry.assign(registry.create(), val++); registry.assign(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.assign(entity, uval++); registry.assign(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, ComponentsWithTypesFromStandardTemplateLibrary) { // see #37 - the test shouldn't crash, that's all entt::registry<> registry; const auto entity = registry.create(); registry.assign>(entity).insert(42); registry.destroy(entity); } TEST(Registry, ConstructWithComponents) { // it should compile, that's all entt::registry<> registry; const auto value = 0; registry.assign(registry.create(), value); } TEST(Registry, MergeTwoRegistries) { using entity_type = entt::registry<>::entity_type; entt::registry<> src; entt::registry<> dst; std::unordered_map ref; auto merge = [&ref](const auto &view, auto &dst) { view.each([&](auto entity, const auto &component) { if(ref.find(entity) == ref.cend()) { const auto other = dst.create(); dst.template assign>(other, component); ref.emplace(entity, other); } else { using component_type = std::decay_t; dst.template assign(ref[entity], component); } }); }; auto e0 = src.create(); src.assign(e0); src.assign(e0); src.assign(e0); auto e1 = src.create(); src.assign(e1); src.assign(e1); src.assign(e1); auto e2 = dst.create(); dst.assign(e2); dst.assign(e2); dst.assign(e2); auto e3 = dst.create(); dst.assign(e3); dst.assign(e3); auto eq = [](auto begin, auto end) { ASSERT_EQ(begin, end); }; auto ne = [](auto begin, auto end) { ASSERT_NE(begin, end); }; eq(dst.view().begin(), dst.view().end()); eq(dst.view().begin(), dst.view().end()); merge(src.view(), dst); merge(src.view(), dst); merge(src.view(), dst); merge(src.view(), dst); ne(dst.view().begin(), dst.view().end()); ne(dst.view().begin(), dst.view().end()); } TEST(Registry, Signals) { entt::registry<> registry; listener listener; registry.construction().connect<&listener::incr>(&listener); registry.destruction().connect<&listener::decr>(&listener); auto e0 = registry.create(); auto e1 = registry.create(); registry.assign(e0); registry.assign(e1); ASSERT_EQ(listener.counter, 2); ASSERT_EQ(listener.last, e1); registry.remove(e0); ASSERT_EQ(listener.counter, 1); ASSERT_EQ(listener.last, e0); registry.destruction().disconnect<&listener::decr>(&listener); registry.remove(e1); ASSERT_EQ(listener.counter, 1); ASSERT_EQ(listener.last, e0); registry.construction().disconnect<&listener::incr>(&listener); registry.assign(e1); ASSERT_EQ(listener.counter, 1); ASSERT_EQ(listener.last, e0); registry.construction().connect<&listener::incr>(&listener); registry.destruction().connect<&listener::decr>(&listener); registry.assign(e0); registry.reset(e1); ASSERT_EQ(listener.counter, 1); ASSERT_EQ(listener.last, e1); registry.reset(); ASSERT_EQ(listener.counter, 0); ASSERT_EQ(listener.last, e0); registry.assign(e0); registry.assign(e1); registry.destroy(e1); ASSERT_EQ(listener.counter, 1); ASSERT_EQ(listener.last, e1); } TEST(Registry, DestroyByComponents) { entt::registry<> registry; const auto e0 = registry.create(); const auto e1 = registry.create(); const auto e2 = registry.create(); registry.assign(e0); registry.assign(e0); registry.assign(e0); registry.assign(e1); registry.assign(e1); registry.assign(e2); ASSERT_TRUE(registry.valid(e0)); ASSERT_TRUE(registry.valid(e1)); ASSERT_TRUE(registry.valid(e2)); registry.destroy(); ASSERT_FALSE(registry.valid(e0)); ASSERT_TRUE(registry.valid(e1)); ASSERT_TRUE(registry.valid(e2)); registry.destroy(); ASSERT_FALSE(registry.valid(e0)); ASSERT_FALSE(registry.valid(e1)); ASSERT_TRUE(registry.valid(e2)); registry.destroy(); ASSERT_FALSE(registry.valid(e0)); ASSERT_FALSE(registry.valid(e1)); ASSERT_FALSE(registry.valid(e2)); } TEST(Registry, SignalsOnAccommodate) { entt::registry<> registry; const auto entity = registry.create(); const auto view = registry.persistent_view(); registry.assign(entity); registry.assign_or_replace(entity); ASSERT_FALSE((view.empty())); } TEST(Registry, CreateManyEntitiesAtOnce) { entt::registry<> registry; entt::registry<>::entity_type entities[3]; const auto entity = registry.create(); registry.destroy(registry.create()); registry.destroy(entity); registry.destroy(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::registry<>::entity_type{0}); ASSERT_EQ(registry.version(entities[0]), entt::registry<>::version_type{2}); ASSERT_EQ(registry.entity(entities[1]), entt::registry<>::entity_type{1}); ASSERT_EQ(registry.version(entities[1]), entt::registry<>::version_type{1}); ASSERT_EQ(registry.entity(entities[2]), entt::registry<>::entity_type{2}); ASSERT_EQ(registry.version(entities[2]), entt::registry<>::version_type{0}); } TEST(Registry, PersistentViewInterleaved) { entt::registry<> registry; typename entt::registry<>::entity_type entity = entt::null; entity = registry.create(); registry.assign(entity); registry.assign(entity); const auto view = registry.persistent_view(); entity = registry.create(); registry.assign(entity); registry.assign(entity); decltype(view)::size_type cnt{0}; view.each([&cnt](auto...) { ++cnt; }); ASSERT_EQ(cnt, decltype(view)::size_type{2}); } TEST(Registry, PersistentViewSortInterleaved) { entt::registry<> registry; const auto view = registry.persistent_view(); const auto e0 = registry.create(); registry.assign(e0, 0); registry.assign(e0, '0'); const auto e1 = registry.create(); registry.assign(e1, 1); registry.assign(e1, '1'); registry.sort([](auto lhs, auto rhs) { return lhs > rhs; }); registry.sort([](auto lhs, auto rhs) { return lhs < rhs; }); const auto e2 = registry.create(); registry.assign(e2, 2); registry.assign(e2, '2'); view.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, Clone) { entt::registry<> registry; entt::registry<> other; const auto entity = other.create(); other.assign(entity, 42); other.assign(entity, 'c'); registry.destroy(registry.create()); const auto e0 = registry.create(); registry.assign(e0, 0); registry.assign(e0, 0.0); const auto e1 = registry.create(); registry.assign(e1, 1); registry.assign(e1, '1'); registry.assign(e1, 1.1); const auto e2 = registry.create(); registry.assign(e2, 2); registry.assign(e2, '2'); registry.destroy(e1); other.clone(registry); ASSERT_FALSE(other.valid(entity)); ASSERT_TRUE(other.valid(e0)); ASSERT_FALSE(other.valid(e1)); ASSERT_TRUE(other.valid(e2)); ASSERT_TRUE((other.has(e0))); ASSERT_FALSE((other.has(e0))); ASSERT_TRUE((other.has(e2))); ASSERT_EQ(other.get(e0), 0); ASSERT_EQ(other.get(e2), 2); ASSERT_EQ(other.get(e2), '2'); }