#include #include #include #include #include #include #include #include #include #include struct empty_type {}; struct boxed_int { int value; }; inline bool operator==(const boxed_int &lhs, const boxed_int &rhs) { return lhs.value == rhs.value; } TEST(NonOwningGroup, Functionalities) { entt::registry registry; auto group = registry.group(entt::get); auto cgroup = std::as_const(registry).group_if_exists(entt::get); ASSERT_TRUE(group.empty()); const auto e0 = registry.create(); registry.emplace(e0, '1'); const auto e1 = registry.create(); registry.emplace(e1, 42); registry.emplace(e1, '2'); ASSERT_FALSE(group.empty()); ASSERT_NO_FATAL_FAILURE(group.begin()++); ASSERT_NO_FATAL_FAILURE(++cgroup.begin()); ASSERT_NO_FATAL_FAILURE([](auto it) { return it++; }(group.rbegin())); ASSERT_NO_FATAL_FAILURE([](auto it) { return ++it; }(cgroup.rbegin())); ASSERT_NE(group.begin(), group.end()); ASSERT_NE(cgroup.begin(), cgroup.end()); ASSERT_NE(group.rbegin(), group.rend()); ASSERT_NE(cgroup.rbegin(), cgroup.rend()); ASSERT_EQ(group.size(), 1u); registry.emplace(e0); ASSERT_EQ(group.size(), 2u); registry.erase(e0); ASSERT_EQ(group.size(), 1u); for(auto entity: group) { ASSERT_EQ(std::get<0>(cgroup.get(entity)), 42); ASSERT_EQ(std::get<1>(group.get(entity)), '2'); ASSERT_EQ(cgroup.get(entity), '2'); } ASSERT_EQ(group.handle().data()[0u], e1); registry.erase(e0); registry.erase(e1); ASSERT_EQ(group.begin(), group.end()); ASSERT_EQ(cgroup.begin(), cgroup.end()); ASSERT_EQ(group.rbegin(), group.rend()); ASSERT_EQ(cgroup.rbegin(), cgroup.rend()); ASSERT_TRUE(group.empty()); ASSERT_NE(group.capacity(), 0u); group.shrink_to_fit(); ASSERT_EQ(group.capacity(), 0u); decltype(group) invalid{}; ASSERT_TRUE(group); ASSERT_TRUE(cgroup); ASSERT_FALSE(invalid); } TEST(NonOwningGroup, Handle) { entt::registry registry; const auto entity = registry.create(); auto group = registry.group(entt::get); auto &&handle = group.handle(); ASSERT_TRUE(handle.empty()); ASSERT_FALSE(handle.contains(entity)); ASSERT_EQ(&handle, &group.handle()); registry.emplace(entity); registry.emplace(entity); ASSERT_FALSE(handle.empty()); ASSERT_TRUE(handle.contains(entity)); ASSERT_EQ(&handle, &group.handle()); } TEST(NonOwningGroup, Invalid) { entt::registry registry{}; auto group = std::as_const(registry).group_if_exists(entt::get); const auto entity = registry.create(); registry.emplace(entity); registry.emplace(entity); ASSERT_FALSE(group); ASSERT_TRUE(group.empty()); ASSERT_EQ(group.size(), 0u); ASSERT_EQ(group.capacity(), 0u); ASSERT_NO_FATAL_FAILURE(group.shrink_to_fit()); ASSERT_EQ(group.begin(), group.end()); ASSERT_EQ(group.rbegin(), group.rend()); ASSERT_FALSE(group.contains(entity)); ASSERT_EQ(group.find(entity), group.end()); ASSERT_EQ(group.front(), entt::entity{entt::null}); ASSERT_EQ(group.back(), entt::entity{entt::null}); } TEST(NonOwningGroup, ElementAccess) { entt::registry registry; auto group = registry.group(entt::get); auto cgroup = std::as_const(registry).group_if_exists(entt::get); const auto e0 = registry.create(); registry.emplace(e0); registry.emplace(e0); const auto e1 = registry.create(); registry.emplace(e1); registry.emplace(e1); for(auto i = 0u; i < group.size(); ++i) { ASSERT_EQ(group[i], i ? e0 : e1); ASSERT_EQ(cgroup[i], i ? e0 : e1); } } TEST(NonOwningGroup, Contains) { entt::registry registry; auto group = registry.group(entt::get); const auto e0 = registry.create(); registry.emplace(e0); registry.emplace(e0); const auto e1 = registry.create(); registry.emplace(e1); registry.emplace(e1); registry.destroy(e0); ASSERT_FALSE(group.contains(e0)); ASSERT_TRUE(group.contains(e1)); } TEST(NonOwningGroup, Empty) { entt::registry registry; const auto e0 = registry.create(); registry.emplace(e0); registry.emplace(e0); registry.emplace(e0); const auto e1 = registry.create(); registry.emplace(e1); registry.emplace(e1); ASSERT_TRUE(registry.group(entt::get).empty()); ASSERT_TRUE(registry.group(entt::get).empty()); } TEST(NonOwningGroup, Each) { entt::registry registry; entt::entity entity[2]{registry.create(), registry.create()}; auto group = registry.group(entt::get); auto cgroup = std::as_const(registry).group_if_exists(entt::get); registry.emplace(entity[0u], 0); registry.emplace(entity[0u], 0); registry.emplace(entity[1u], 1); registry.emplace(entity[1u], 1); auto iterable = group.each(); auto citerable = cgroup.each(); ASSERT_NE(citerable.begin(), citerable.end()); ASSERT_NO_FATAL_FAILURE(iterable.begin()->operator=(*iterable.begin())); ASSERT_EQ(decltype(iterable.end()){}, iterable.end()); auto it = iterable.begin(); ASSERT_EQ((it++, ++it), iterable.end()); group.each([expected = 1u](auto entt, int &ivalue, char &cvalue) mutable { ASSERT_EQ(entt::to_integral(entt), expected); ASSERT_EQ(ivalue, expected); ASSERT_EQ(cvalue, expected); --expected; }); cgroup.each([expected = 1u](const int &ivalue, const char &cvalue) mutable { ASSERT_EQ(ivalue, expected); ASSERT_EQ(cvalue, expected); --expected; }); ASSERT_EQ(std::get<0>(*iterable.begin()), entity[1u]); ASSERT_EQ(std::get<0>(*++citerable.begin()), entity[0u]); static_assert(std::is_same_v(*iterable.begin())), int &>); static_assert(std::is_same_v(*citerable.begin())), const char &>); // do not use iterable, make sure an iterable group works when created from a temporary for(auto [entt, ivalue, cvalue]: registry.group(entt::get).each()) { ASSERT_EQ(entt::to_integral(entt), ivalue); ASSERT_EQ(entt::to_integral(entt), cvalue); } } TEST(NonOwningGroup, Sort) { entt::registry registry; auto group = registry.group(entt::get); const auto e0 = registry.create(); const auto e1 = registry.create(); const auto e2 = registry.create(); const auto e3 = registry.create(); registry.emplace(e0, 0u); registry.emplace(e1, 1u); registry.emplace(e2, 2u); registry.emplace(e3, 3u); registry.emplace(e0, 0); registry.emplace(e1, 1); registry.emplace(e2, 2); ASSERT_EQ(group.handle().data()[0u], e0); ASSERT_EQ(group.handle().data()[1u], e1); ASSERT_EQ(group.handle().data()[2u], e2); group.sort([](const entt::entity lhs, const entt::entity rhs) { return entt::to_integral(lhs) < entt::to_integral(rhs); }); ASSERT_EQ(group.handle().data()[0u], e2); ASSERT_EQ(group.handle().data()[1u], e1); ASSERT_EQ(group.handle().data()[2u], e0); ASSERT_EQ((group.get(e0)), (std::make_tuple(0, 0u))); ASSERT_EQ((group.get(e1)), (std::make_tuple(1, 1u))); ASSERT_EQ((group.get(e2)), (std::make_tuple(2, 2u))); ASSERT_FALSE(group.contains(e3)); group.sort([](const int lhs, const int rhs) { return lhs > rhs; }); ASSERT_EQ(group.handle().data()[0u], e0); ASSERT_EQ(group.handle().data()[1u], e1); ASSERT_EQ(group.handle().data()[2u], e2); ASSERT_EQ((group.get(e0)), (std::make_tuple(0, 0u))); ASSERT_EQ((group.get(e1)), (std::make_tuple(1, 1u))); ASSERT_EQ((group.get(e2)), (std::make_tuple(2, 2u))); ASSERT_FALSE(group.contains(e3)); group.sort([](const auto lhs, const auto rhs) { static_assert(std::is_same_v(lhs)), const int &>); static_assert(std::is_same_v(rhs)), unsigned int &>); return std::get<0>(lhs) < std::get<0>(rhs); }); ASSERT_EQ(group.handle().data()[0u], e2); ASSERT_EQ(group.handle().data()[1u], e1); ASSERT_EQ(group.handle().data()[2u], e0); ASSERT_EQ((group.get(e0)), (std::make_tuple(0, 0u))); ASSERT_EQ((group.get(e1)), (std::make_tuple(1, 1u))); ASSERT_EQ((group.get(e2)), (std::make_tuple(2, 2u))); ASSERT_FALSE(group.contains(e3)); } TEST(NonOwningGroup, SortAsAPool) { entt::registry registry; auto group = registry.group(entt::get); const auto e0 = registry.create(); const auto e1 = registry.create(); const auto e2 = registry.create(); const auto e3 = registry.create(); auto uval = 0u; auto ival = 0; registry.emplace(e0, uval++); registry.emplace(e1, uval++); registry.emplace(e2, uval++); registry.emplace(e3, uval + 1); registry.emplace(e0, ival++); registry.emplace(e1, ival++); registry.emplace(e2, ival++); for(auto entity: group) { ASSERT_EQ(group.get(entity), --uval); ASSERT_EQ(group.get(entity), --ival); } registry.sort(std::less{}); group.sort(); ASSERT_EQ((group.get(e0)), (std::make_tuple(0, 0u))); ASSERT_EQ((group.get(e1)), (std::make_tuple(1, 1u))); ASSERT_EQ((group.get(e2)), (std::make_tuple(2, 2u))); ASSERT_FALSE(group.contains(e3)); for(auto entity: group) { ASSERT_EQ(group.get(entity), uval++); ASSERT_EQ(group.get(entity), ival++); } } TEST(NonOwningGroup, IndexRebuiltOnDestroy) { entt::registry registry; auto group = registry.group(entt::get); const auto e0 = registry.create(); const auto e1 = registry.create(); registry.emplace(e0, 0u); registry.emplace(e1, 1u); registry.emplace(e0, 0); registry.emplace(e1, 1); registry.destroy(e0); registry.emplace(registry.create(), 42); ASSERT_EQ(group.size(), 1u); ASSERT_EQ(group[{}], e1); ASSERT_EQ(group.get(e1), 1); ASSERT_EQ(group.get(e1), 1u); group.each([e1](auto entity, auto ivalue, auto uivalue) { ASSERT_EQ(entity, e1); ASSERT_EQ(ivalue, 1); ASSERT_EQ(uivalue, 1u); }); for(auto &&curr: group.each()) { ASSERT_EQ(std::get<0>(curr), e1); ASSERT_EQ(std::get<1>(curr), 1); ASSERT_EQ(std::get<2>(curr), 1u); } } TEST(NonOwningGroup, ConstNonConstAndAllInBetween) { entt::registry registry; auto group = registry.group(entt::get); ASSERT_EQ(group.size(), 0u); const auto entity = registry.create(); registry.emplace(entity, 0); registry.emplace(entity); registry.emplace(entity, 'c'); ASSERT_EQ(group.size(), 1u); 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>); static_assert(std::is_same_v)), decltype(std::as_const(registry).group_if_exists(entt::get))>); static_assert(std::is_same_v)), decltype(std::as_const(registry).group_if_exists(entt::get))>); static_assert(std::is_same_v)), decltype(std::as_const(registry).group_if_exists(entt::get))>); group.each([](auto &&i, auto &&c) { static_assert(std::is_same_v); static_assert(std::is_same_v); }); for(auto [entt, iv, cv]: group.each()) { static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); } } TEST(NonOwningGroup, Find) { entt::registry registry; auto group = registry.group(entt::get); const auto e0 = registry.create(); registry.emplace(e0); registry.emplace(e0); const auto e1 = registry.create(); registry.emplace(e1); registry.emplace(e1); const auto e2 = registry.create(); registry.emplace(e2); registry.emplace(e2); const auto e3 = registry.create(); registry.emplace(e3); registry.emplace(e3); registry.erase(e1); ASSERT_NE(group.find(e0), group.end()); ASSERT_EQ(group.find(e1), group.end()); ASSERT_NE(group.find(e2), group.end()); ASSERT_NE(group.find(e3), group.end()); auto it = group.find(e2); ASSERT_EQ(*it, e2); ASSERT_EQ(*(++it), e3); ASSERT_EQ(*(++it), e0); ASSERT_EQ(++it, group.end()); ASSERT_EQ(++group.find(e0), group.end()); const auto e4 = registry.create(); registry.destroy(e4); const auto e5 = registry.create(); registry.emplace(e5); registry.emplace(e5); ASSERT_NE(group.find(e5), group.end()); ASSERT_EQ(group.find(e4), group.end()); } TEST(NonOwningGroup, ExcludedComponents) { entt::registry registry; const auto e0 = registry.create(); registry.emplace(e0, 0); const auto e1 = registry.create(); registry.emplace(e1, 1); registry.emplace(e1); const auto group = registry.group(entt::get, entt::exclude); const auto e2 = registry.create(); registry.emplace(e2, 2); const auto e3 = registry.create(); registry.emplace(e3, 3); registry.emplace(e3); for(const auto entity: group) { ASSERT_TRUE(entity == e0 || entity == e2); if(entity == e0) { ASSERT_EQ(group.get(e0), 0); } else if(entity == e2) { ASSERT_EQ(group.get(e2), 2); } } registry.emplace(e0); registry.emplace(e2); ASSERT_TRUE(group.empty()); registry.erase(e1); registry.erase(e3); for(const auto entity: group) { ASSERT_TRUE(entity == e1 || entity == e3); if(entity == e1) { ASSERT_EQ(group.get(e1), 1); } else if(entity == e3) { ASSERT_EQ(group.get(e3), 3); } } } TEST(NonOwningGroup, EmptyAndNonEmptyTypes) { entt::registry registry; const auto group = registry.group(entt::get); const auto e0 = registry.create(); registry.emplace(e0); registry.emplace(e0); const auto e1 = registry.create(); registry.emplace(e1); registry.emplace(e1); registry.emplace(registry.create()); for(const auto entity: group) { ASSERT_TRUE(entity == e0 || entity == e1); } group.each([e0, e1](const auto entity, const int &) { ASSERT_TRUE(entity == e0 || entity == e1); }); for(auto [entt, iv]: group.each()) { static_assert(std::is_same_v); static_assert(std::is_same_v); ASSERT_TRUE(entt == e0 || entt == e1); } ASSERT_EQ(group.size(), 2u); } TEST(NonOwningGroup, TrackEntitiesOnComponentDestruction) { entt::registry registry; const auto group = registry.group(entt::get, entt::exclude); const auto cgroup = std::as_const(registry).group_if_exists(entt::get, entt::exclude); const auto entity = registry.create(); registry.emplace(entity); registry.emplace(entity); ASSERT_TRUE(group.empty()); ASSERT_TRUE(cgroup.empty()); registry.erase(entity); ASSERT_FALSE(group.empty()); ASSERT_FALSE(cgroup.empty()); } TEST(NonOwningGroup, EmptyTypes) { entt::registry registry; const auto entity = registry.create(); registry.emplace(entity); registry.emplace(entity); registry.emplace(entity); registry.group(entt::get).each([entity](const auto entt, int, char) { ASSERT_EQ(entity, entt); }); for(auto [entt, iv, cv]: registry.group(entt::get).each()) { static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); ASSERT_EQ(entity, entt); } registry.group(entt::get).each([check = true](int, char) mutable { ASSERT_TRUE(check); check = false; }); for(auto [entt, iv, cv]: registry.group(entt::get).each()) { static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); ASSERT_EQ(entity, entt); } registry.group(entt::get).each([entity](const auto entt, int, char) { ASSERT_EQ(entity, entt); }); for(auto [entt, iv, cv]: registry.group(entt::get).each()) { static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); ASSERT_EQ(entity, entt); } auto iterable = registry.group(entt::get).each(); ASSERT_EQ(iterable.begin(), iterable.end()); } TEST(NonOwningGroup, FrontBack) { entt::registry registry; auto group = registry.group<>(entt::get); ASSERT_EQ(group.front(), static_cast(entt::null)); ASSERT_EQ(group.back(), static_cast(entt::null)); const auto e0 = registry.create(); registry.emplace(e0); registry.emplace(e0); const auto e1 = registry.create(); registry.emplace(e1); registry.emplace(e1); const auto entity = registry.create(); registry.emplace(entity); ASSERT_EQ(group.front(), e1); ASSERT_EQ(group.back(), e0); } TEST(NonOwningGroup, SignalRace) { entt::registry registry; registry.on_construct().connect<&entt::registry::emplace_or_replace>(); const auto group = registry.group(entt::get); auto entity = registry.create(); registry.emplace(entity); ASSERT_EQ(group.size(), 1u); } TEST(NonOwningGroup, ExtendedGet) { using type = decltype(std::declval().group(entt::get).get({})); static_assert(std::tuple_size_v == 2u); static_assert(std::is_same_v, int &>); static_assert(std::is_same_v, char &>); entt::registry registry; const auto entity = registry.create(); registry.emplace(entity, 42); registry.emplace(entity, 'c'); const auto tup = registry.group(entt::get).get(entity); ASSERT_EQ(std::get<0>(tup), 42); ASSERT_EQ(std::get<1>(tup), 'c'); } TEST(NonOwningGroup, IterableGroupAlgorithmCompatibility) { entt::registry registry; const auto entity = registry.create(); registry.emplace(entity); registry.emplace(entity); const auto group = registry.group(entt::get); const auto iterable = group.each(); const auto it = std::find_if(iterable.begin(), iterable.end(), [entity](auto args) { return std::get<0>(args) == entity; }); ASSERT_EQ(std::get<0>(*it), entity); } TEST(NonOwningGroup, Storage) { entt::registry registry; const auto entity = registry.create(); const auto group = registry.group(entt::get); static_assert(std::is_same_v()), entt::storage_type_t &>); 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()), const entt::storage_type_t &>); ASSERT_EQ(group.size(), 0u); group.storage().emplace(entity); registry.emplace(entity); ASSERT_EQ(group.size(), 1u); ASSERT_TRUE(group.storage().contains(entity)); ASSERT_TRUE(group.storage().contains(entity)); ASSERT_TRUE((registry.all_of(entity))); group.storage<0u>().erase(entity); ASSERT_EQ(group.size(), 0u); ASSERT_TRUE(group.storage<1u>().contains(entity)); ASSERT_FALSE((registry.all_of(entity))); } TEST(OwningGroup, Functionalities) { entt::registry registry; auto group = registry.group(entt::get); auto cgroup = std::as_const(registry).group_if_exists(entt::get); ASSERT_TRUE(group.empty()); const auto e0 = registry.create(); registry.emplace(e0, '1'); const auto e1 = registry.create(); registry.emplace(e1, 42); registry.emplace(e1, '2'); ASSERT_FALSE(group.empty()); ASSERT_NO_FATAL_FAILURE(group.begin()++); ASSERT_NO_FATAL_FAILURE(++cgroup.begin()); ASSERT_NO_FATAL_FAILURE([](auto it) { return it++; }(group.rbegin())); ASSERT_NO_FATAL_FAILURE([](auto it) { return ++it; }(cgroup.rbegin())); ASSERT_NE(group.begin(), group.end()); ASSERT_NE(cgroup.begin(), cgroup.end()); ASSERT_NE(group.rbegin(), group.rend()); ASSERT_NE(cgroup.rbegin(), cgroup.rend()); ASSERT_EQ(group.size(), 1u); registry.emplace(e0); ASSERT_EQ(group.size(), 2u); registry.erase(e0); ASSERT_EQ(group.size(), 1u); ASSERT_EQ(cgroup.storage().raw()[0u][0u], 42); ASSERT_EQ(group.storage().raw()[0u][0u], 42); for(auto entity: group) { ASSERT_EQ(std::get<0>(cgroup.get(entity)), 42); ASSERT_EQ(std::get<1>(group.get(entity)), '2'); ASSERT_EQ(cgroup.get(entity), '2'); } ASSERT_EQ(group.storage().raw()[0u][0u], 42); registry.erase(e0); registry.erase(e1); ASSERT_EQ(group.begin(), group.end()); ASSERT_EQ(cgroup.begin(), cgroup.end()); ASSERT_EQ(group.rbegin(), group.rend()); ASSERT_EQ(cgroup.rbegin(), cgroup.rend()); ASSERT_TRUE(group.empty()); decltype(group) invalid{}; ASSERT_TRUE(group); ASSERT_TRUE(cgroup); ASSERT_FALSE(invalid); } TEST(OwningGroup, Invalid) { entt::registry registry{}; auto group = std::as_const(registry).group_if_exists(entt::get); const auto entity = registry.create(); registry.emplace(entity); registry.emplace(entity); ASSERT_FALSE(group); ASSERT_TRUE(group.empty()); ASSERT_EQ(group.size(), 0u); ASSERT_EQ(group.begin(), group.end()); ASSERT_EQ(group.rbegin(), group.rend()); ASSERT_FALSE(group.contains(entity)); ASSERT_EQ(group.find(entity), group.end()); ASSERT_EQ(group.front(), entt::entity{entt::null}); ASSERT_EQ(group.back(), entt::entity{entt::null}); } TEST(OwningGroup, ElementAccess) { entt::registry registry; auto group = registry.group(entt::get); auto cgroup = std::as_const(registry).group_if_exists(entt::get); const auto e0 = registry.create(); registry.emplace(e0); registry.emplace(e0); const auto e1 = registry.create(); registry.emplace(e1); registry.emplace(e1); for(auto i = 0u; i < group.size(); ++i) { ASSERT_EQ(group[i], i ? e0 : e1); ASSERT_EQ(cgroup[i], i ? e0 : e1); } } TEST(OwningGroup, Contains) { entt::registry registry; auto group = registry.group(entt::get); const auto e0 = registry.create(); registry.emplace(e0); registry.emplace(e0); const auto e1 = registry.create(); registry.emplace(e1); registry.emplace(e1); registry.destroy(e0); ASSERT_FALSE(group.contains(e0)); ASSERT_TRUE(group.contains(e1)); } TEST(OwningGroup, Empty) { entt::registry registry; const auto e0 = registry.create(); registry.emplace(e0); registry.emplace(e0); registry.emplace(e0); const auto e1 = registry.create(); registry.emplace(e1); registry.emplace(e1); ASSERT_TRUE((registry.group(entt::get).empty())); ASSERT_TRUE((registry.group(entt::get).empty())); } TEST(OwningGroup, Each) { entt::registry registry; entt::entity entity[2]{registry.create(), registry.create()}; auto group = registry.group(entt::get); auto cgroup = std::as_const(registry).group_if_exists(entt::get); registry.emplace(entity[0u], 0); registry.emplace(entity[0u], 0); registry.emplace(entity[1u], 1); registry.emplace(entity[1u], 1); auto iterable = group.each(); auto citerable = cgroup.each(); ASSERT_NE(citerable.begin(), citerable.end()); ASSERT_NO_FATAL_FAILURE(iterable.begin()->operator=(*iterable.begin())); ASSERT_EQ(decltype(iterable.end()){}, iterable.end()); auto it = iterable.begin(); ASSERT_EQ((it++, ++it), iterable.end()); group.each([expected = 1u](auto entt, int &ivalue, char &cvalue) mutable { ASSERT_EQ(entt::to_integral(entt), expected); ASSERT_EQ(ivalue, expected); ASSERT_EQ(cvalue, expected); --expected; }); cgroup.each([expected = 1u](const int &ivalue, const char &cvalue) mutable { ASSERT_EQ(ivalue, expected); ASSERT_EQ(cvalue, expected); --expected; }); ASSERT_EQ(std::get<0>(*iterable.begin()), entity[1u]); ASSERT_EQ(std::get<0>(*++citerable.begin()), entity[0u]); static_assert(std::is_same_v(*iterable.begin())), int &>); static_assert(std::is_same_v(*citerable.begin())), const char &>); // do not use iterable, make sure an iterable group works when created from a temporary for(auto [entt, ivalue, cvalue]: registry.group(entt::get).each()) { ASSERT_EQ(entt::to_integral(entt), ivalue); ASSERT_EQ(entt::to_integral(entt), cvalue); } } TEST(OwningGroup, SortOrdered) { entt::registry registry; auto group = registry.group(); entt::entity entities[5]{}; registry.create(std::begin(entities), std::end(entities)); registry.emplace(entities[0], 12); registry.emplace(entities[0], 'a'); registry.emplace(entities[1], 9); registry.emplace(entities[1], 'b'); registry.emplace(entities[2], 6); registry.emplace(entities[2], 'c'); registry.emplace(entities[3], 1); registry.emplace(entities[4], 2); group.sort([&group](const entt::entity lhs, const entt::entity rhs) { return group.get(lhs).value < group.get(rhs).value; }); ASSERT_EQ(group.storage().data()[0u], entities[0]); ASSERT_EQ(group.storage().data()[1u], entities[1]); ASSERT_EQ(group.storage().data()[2u], entities[2]); ASSERT_EQ(group.storage().data()[3u], entities[3]); ASSERT_EQ(group.storage().data()[4u], entities[4]); ASSERT_EQ(group.storage().raw()[0u][0u].value, 12); ASSERT_EQ(group.storage().raw()[0u][1u].value, 9); ASSERT_EQ(group.storage().raw()[0u][2u].value, 6); ASSERT_EQ(group.storage().raw()[0u][3u].value, 1); ASSERT_EQ(group.storage().raw()[0u][4u].value, 2); ASSERT_EQ(group.storage().raw()[0u][0u], 'a'); ASSERT_EQ(group.storage().raw()[0u][1u], 'b'); ASSERT_EQ(group.storage().raw()[0u][2u], 'c'); ASSERT_EQ((group.get(entities[0])), (std::make_tuple(boxed_int{12}, 'a'))); ASSERT_EQ((group.get(entities[1])), (std::make_tuple(boxed_int{9}, 'b'))); ASSERT_EQ((group.get(entities[2])), (std::make_tuple(boxed_int{6}, 'c'))); ASSERT_FALSE(group.contains(entities[3])); ASSERT_FALSE(group.contains(entities[4])); } TEST(OwningGroup, SortReverse) { entt::registry registry; auto group = registry.group(); entt::entity entities[5]{}; registry.create(std::begin(entities), std::end(entities)); registry.emplace(entities[0], 6); registry.emplace(entities[0], 'a'); registry.emplace(entities[1], 9); registry.emplace(entities[1], 'b'); registry.emplace(entities[2], 12); registry.emplace(entities[2], 'c'); registry.emplace(entities[3], 1); registry.emplace(entities[4], 2); group.sort([](const auto &lhs, const auto &rhs) { return lhs.value < rhs.value; }); ASSERT_EQ(group.storage().data()[0u], entities[2]); ASSERT_EQ(group.storage().data()[1u], entities[1]); ASSERT_EQ(group.storage().data()[2u], entities[0]); ASSERT_EQ(group.storage().data()[3u], entities[3]); ASSERT_EQ(group.storage().data()[4u], entities[4]); ASSERT_EQ(group.storage().raw()[0u][0u].value, 12); ASSERT_EQ(group.storage().raw()[0u][1u].value, 9); ASSERT_EQ(group.storage().raw()[0u][2u].value, 6); ASSERT_EQ(group.storage().raw()[0u][3u].value, 1); ASSERT_EQ(group.storage().raw()[0u][4u].value, 2); ASSERT_EQ(group.storage().raw()[0u][0u], 'c'); ASSERT_EQ(group.storage().raw()[0u][1u], 'b'); ASSERT_EQ(group.storage().raw()[0u][2u], 'a'); ASSERT_EQ((group.get(entities[0])), (std::make_tuple(boxed_int{6}, 'a'))); ASSERT_EQ((group.get(entities[1])), (std::make_tuple(boxed_int{9}, 'b'))); ASSERT_EQ((group.get(entities[2])), (std::make_tuple(boxed_int{12}, 'c'))); ASSERT_FALSE(group.contains(entities[3])); ASSERT_FALSE(group.contains(entities[4])); } TEST(OwningGroup, SortUnordered) { entt::registry registry; auto group = registry.group(entt::get); entt::entity entities[7]{}; registry.create(std::begin(entities), std::end(entities)); registry.emplace(entities[0], 6); registry.emplace(entities[0], 'c'); registry.emplace(entities[1], 3); registry.emplace(entities[1], 'b'); registry.emplace(entities[2], 1); registry.emplace(entities[2], 'a'); registry.emplace(entities[3], 9); registry.emplace(entities[3], 'd'); registry.emplace(entities[4], 12); registry.emplace(entities[4], 'e'); registry.emplace(entities[5], 4); registry.emplace(entities[6], 5); group.sort([](const auto lhs, const auto rhs) { static_assert(std::is_same_v(lhs)), boxed_int &>); static_assert(std::is_same_v(rhs)), char &>); return std::get<1>(lhs) < std::get<1>(rhs); }); ASSERT_EQ(group.storage().data()[0u], entities[4]); ASSERT_EQ(group.storage().data()[1u], entities[3]); ASSERT_EQ(group.storage().data()[2u], entities[0]); ASSERT_EQ(group.storage().data()[3u], entities[1]); ASSERT_EQ(group.storage().data()[4u], entities[2]); ASSERT_EQ(group.storage().data()[5u], entities[5]); ASSERT_EQ(group.storage().data()[6u], entities[6]); ASSERT_EQ(group.storage().raw()[0u][0u].value, 12); ASSERT_EQ(group.storage().raw()[0u][1u].value, 9); ASSERT_EQ(group.storage().raw()[0u][2u].value, 6); ASSERT_EQ(group.storage().raw()[0u][3u].value, 3); ASSERT_EQ(group.storage().raw()[0u][4u].value, 1); ASSERT_EQ(group.storage().raw()[0u][5u].value, 4); ASSERT_EQ(group.storage().raw()[0u][6u].value, 5); ASSERT_EQ(group.get(group.storage().data()[0u]), 'e'); ASSERT_EQ(group.get(group.storage().data()[1u]), 'd'); ASSERT_EQ(group.get(group.storage().data()[2u]), 'c'); ASSERT_EQ(group.get(group.storage().data()[3u]), 'b'); ASSERT_EQ(group.get(group.storage().data()[4u]), 'a'); ASSERT_FALSE(group.contains(entities[5])); ASSERT_FALSE(group.contains(entities[6])); } TEST(OwningGroup, SortWithExclusionList) { entt::registry registry; auto group = registry.group({}, entt::exclude); entt::entity entities[5]{}; registry.create(std::begin(entities), std::end(entities)); registry.emplace(entities[0], 0); registry.emplace(entities[1], 1); registry.emplace(entities[2], 2); registry.emplace(entities[3], 3); registry.emplace(entities[4], 4); registry.emplace(entities[2]); group.sort([](const entt::entity lhs, const entt::entity rhs) { return lhs < rhs; }); ASSERT_EQ(group.storage().data()[0u], entities[4]); ASSERT_EQ(group.storage().data()[1u], entities[3]); ASSERT_EQ(group.storage().data()[2u], entities[1]); ASSERT_EQ(group.storage().data()[3u], entities[0]); ASSERT_EQ(group.storage().raw()[0u][0u].value, 4); ASSERT_EQ(group.storage().raw()[0u][1u].value, 3); ASSERT_EQ(group.storage().raw()[0u][2u].value, 1); ASSERT_EQ(group.storage().raw()[0u][3u].value, 0); ASSERT_EQ(group.get(entities[0]).value, 0); ASSERT_EQ(group.get(entities[1]).value, 1); ASSERT_EQ(group.get(entities[3]).value, 3); ASSERT_EQ(group.get(entities[4]).value, 4); ASSERT_FALSE(group.contains(entities[2])); } TEST(OwningGroup, IndexRebuiltOnDestroy) { entt::registry registry; auto group = registry.group(entt::get); const auto e0 = registry.create(); const auto e1 = registry.create(); registry.emplace(e0, 0u); registry.emplace(e1, 1u); registry.emplace(e0, 0); registry.emplace(e1, 1); registry.destroy(e0); registry.emplace(registry.create(), 42); ASSERT_EQ(group.size(), 1u); ASSERT_EQ(group[{}], e1); ASSERT_EQ(group.get(e1), 1); ASSERT_EQ(group.get(e1), 1u); group.each([e1](auto entity, auto ivalue, auto uivalue) { ASSERT_EQ(entity, e1); ASSERT_EQ(ivalue, 1); ASSERT_EQ(uivalue, 1u); }); for(auto &&curr: group.each()) { ASSERT_EQ(std::get<0>(curr), e1); ASSERT_EQ(std::get<1>(curr), 1); ASSERT_EQ(std::get<2>(curr), 1u); } } TEST(OwningGroup, ConstNonConstAndAllInBetween) { entt::registry registry; auto group = registry.group(entt::get); ASSERT_EQ(group.size(), 0u); const auto entity = registry.create(); registry.emplace(entity, 0); registry.emplace(entity, 'c'); registry.emplace(entity); registry.emplace(entity, 0.); registry.emplace(entity, 0.f); ASSERT_EQ(group.size(), 1u); static_assert(std::is_same_v({})), int &>); static_assert(std::is_same_v({})), const char &>); static_assert(std::is_same_v({})), double &>); static_assert(std::is_same_v({})), const float &>); static_assert(std::is_same_v({})), std::tuple>); static_assert(std::is_same_v>); static_assert(std::is_same_v(entt::get)), decltype(std::as_const(registry).group_if_exists(entt::get))>); static_assert(std::is_same_v(entt::get)), decltype(std::as_const(registry).group_if_exists(entt::get))>); static_assert(std::is_same_v(entt::get)), decltype(std::as_const(registry).group_if_exists(entt::get))>); group.each([](auto &&i, auto &&c, auto &&d, auto &&f) { static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); }); for(auto [entt, iv, cv, dv, fv]: group.each()) { static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); } } TEST(OwningGroup, Find) { entt::registry registry; auto group = registry.group(entt::get); const auto e0 = registry.create(); registry.emplace(e0); registry.emplace(e0); const auto e1 = registry.create(); registry.emplace(e1); registry.emplace(e1); const auto e2 = registry.create(); registry.emplace(e2); registry.emplace(e2); const auto e3 = registry.create(); registry.emplace(e3); registry.emplace(e3); registry.erase(e1); ASSERT_NE(group.find(e0), group.end()); ASSERT_EQ(group.find(e1), group.end()); ASSERT_NE(group.find(e2), group.end()); ASSERT_NE(group.find(e3), group.end()); auto it = group.find(e2); ASSERT_EQ(*it, e2); ASSERT_EQ(*(++it), e3); ASSERT_EQ(*(++it), e0); ASSERT_EQ(++it, group.end()); ASSERT_EQ(++group.find(e0), group.end()); const auto e4 = registry.create(); registry.destroy(e4); const auto e5 = registry.create(); registry.emplace(e5); registry.emplace(e5); ASSERT_NE(group.find(e5), group.end()); ASSERT_EQ(group.find(e4), group.end()); } TEST(OwningGroup, ExcludedComponents) { entt::registry registry; const auto e0 = registry.create(); registry.emplace(e0, 0); const auto e1 = registry.create(); registry.emplace(e1, 1); registry.emplace(e1); const auto group = registry.group({}, entt::exclude); const auto e2 = registry.create(); registry.emplace(e2, 2); const auto e3 = registry.create(); registry.emplace(e3, 3); registry.emplace(e3); for(const auto entity: group) { ASSERT_TRUE(entity == e0 || entity == e2); if(entity == e0) { ASSERT_EQ(group.get(e0), 0); } else if(entity == e2) { ASSERT_EQ(group.get(e2), 2); } } registry.emplace(e0); registry.emplace(e2); ASSERT_TRUE(group.empty()); registry.erase(e1); registry.erase(e3); for(const auto entity: group) { ASSERT_TRUE(entity == e1 || entity == e3); if(entity == e1) { ASSERT_EQ(group.get(e1), 1); } else if(entity == e3) { ASSERT_EQ(group.get(e3), 3); } } } TEST(OwningGroup, EmptyAndNonEmptyTypes) { entt::registry registry; const auto group = registry.group(entt::get); const auto e0 = registry.create(); registry.emplace(e0); registry.emplace(e0); const auto e1 = registry.create(); registry.emplace(e1); registry.emplace(e1); registry.emplace(registry.create()); for(const auto entity: group) { ASSERT_TRUE(entity == e0 || entity == e1); } group.each([e0, e1](const auto entity, const int &) { ASSERT_TRUE(entity == e0 || entity == e1); }); for(auto [entt, iv]: group.each()) { static_assert(std::is_same_v); static_assert(std::is_same_v); ASSERT_TRUE(entt == e0 || entt == e1); } ASSERT_EQ(group.size(), 2u); } TEST(OwningGroup, TrackEntitiesOnComponentDestruction) { entt::registry registry; const auto group = registry.group({}, entt::exclude); const auto cgroup = std::as_const(registry).group_if_exists({}, entt::exclude); const auto entity = registry.create(); registry.emplace(entity); registry.emplace(entity); ASSERT_TRUE(group.empty()); ASSERT_TRUE(cgroup.empty()); registry.erase(entity); ASSERT_FALSE(group.empty()); ASSERT_FALSE(cgroup.empty()); } TEST(OwningGroup, EmptyTypes) { entt::registry registry; const auto entity = registry.create(); registry.emplace(entity); registry.emplace(entity); registry.emplace(entity); registry.group(entt::get).each([entity](const auto entt, int, char) { ASSERT_EQ(entity, entt); }); for(auto [entt, iv, cv]: registry.group(entt::get).each()) { static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); ASSERT_EQ(entity, entt); } registry.group(entt::get).each([check = true](char, int) mutable { ASSERT_TRUE(check); check = false; }); for(auto [entt, cv, iv]: registry.group(entt::get).each()) { static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); ASSERT_EQ(entity, entt); } registry.group(entt::get).each([entity](const auto entt, int, char) { ASSERT_EQ(entity, entt); }); for(auto [entt, iv, cv]: registry.group(entt::get).each()) { static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); ASSERT_EQ(entity, entt); } auto iterable = registry.group(entt::get).each(); ASSERT_EQ(iterable.begin(), iterable.end()); } TEST(OwningGroup, FrontBack) { entt::registry registry; auto group = registry.group(entt::get); ASSERT_EQ(group.front(), static_cast(entt::null)); ASSERT_EQ(group.back(), static_cast(entt::null)); const auto e0 = registry.create(); registry.emplace(e0); registry.emplace(e0); const auto e1 = registry.create(); registry.emplace(e1); registry.emplace(e1); const auto entity = registry.create(); registry.emplace(entity); ASSERT_EQ(group.front(), e1); ASSERT_EQ(group.back(), e0); } TEST(OwningGroup, SignalRace) { entt::registry registry; registry.on_construct().connect<&entt::registry::emplace_or_replace>(); const auto group = registry.group(entt::get); auto entity = registry.create(); registry.emplace(entity); ASSERT_EQ(group.size(), 1u); } TEST(OwningGroup, StableLateInitialization) { entt::registry registry; for(std::size_t i{}; i < 30u; ++i) { auto entity = registry.create(); if(!(i % 2u)) registry.emplace(entity); if(!(i % 3u)) registry.emplace(entity); } // thanks to @pgruenbacher for pointing out this corner case ASSERT_EQ((registry.group().size()), 5u); } TEST(OwningGroup, PreventEarlyOptOut) { entt::registry registry; registry.emplace(registry.create(), 3); const auto entity = registry.create(); registry.emplace(entity, 'c'); registry.emplace(entity, 2); // thanks to @pgruenbacher for pointing out this corner case registry.group().each([entity](const auto entt, const auto &c, const auto &i) { ASSERT_EQ(entity, entt); ASSERT_EQ(c, 'c'); ASSERT_EQ(i, 2); }); } TEST(OwningGroup, SwappingValuesIsAllowed) { entt::registry registry; const auto group = registry.group(entt::get); for(std::size_t i{}; i < 2u; ++i) { const auto entity = registry.create(); registry.emplace(entity, static_cast(i)); registry.emplace(entity); } registry.destroy(group.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.value); }); } TEST(OwningGroup, ExtendedGet) { using type = decltype(std::declval().group(entt::get).get({})); static_assert(std::tuple_size_v == 2u); static_assert(std::is_same_v, int &>); static_assert(std::is_same_v, char &>); entt::registry registry; const auto entity = registry.create(); registry.emplace(entity, 42); registry.emplace(entity, 'c'); const auto tup = registry.group(entt::get).get(entity); ASSERT_EQ(std::get<0>(tup), 42); ASSERT_EQ(std::get<1>(tup), 'c'); } TEST(OwningGroup, IterableGroupAlgorithmCompatibility) { entt::registry registry; const auto entity = registry.create(); registry.emplace(entity); registry.emplace(entity); const auto group = registry.group(entt::get); const auto iterable = group.each(); const auto it = std::find_if(iterable.begin(), iterable.end(), [entity](auto args) { return std::get<0>(args) == entity; }); ASSERT_EQ(std::get<0>(*it), entity); } TEST(OwningGroup, Storage) { entt::registry registry; const auto entity = registry.create(); const auto group = registry.group(entt::get); static_assert(std::is_same_v()), entt::storage_type_t &>); 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()), const entt::storage_type_t &>); ASSERT_EQ(group.size(), 0u); group.storage().emplace(entity); registry.emplace(entity); ASSERT_EQ(group.size(), 1u); ASSERT_TRUE(group.storage().contains(entity)); ASSERT_TRUE(group.storage().contains(entity)); ASSERT_TRUE((registry.all_of(entity))); group.storage<0u>().erase(entity); ASSERT_EQ(group.size(), 0u); ASSERT_TRUE(group.storage<1u>().contains(entity)); ASSERT_FALSE((registry.all_of(entity))); }