#include #include #include #include #include #include #include #include struct empty_type {}; struct stable_type { static constexpr auto in_place_delete = true; int value; }; TEST(SingleComponentView, Functionalities) { entt::registry registry; auto view = registry.view(); auto cview = std::as_const(registry).view(); const auto e0 = registry.create(); const auto e1 = registry.create(); ASSERT_TRUE(view.empty()); registry.emplace(e1); ASSERT_NO_FATAL_FAILURE(view.begin()++); ASSERT_NO_FATAL_FAILURE(++cview.begin()); ASSERT_NO_FATAL_FAILURE([](auto it) { return it++; }(view.rbegin())); ASSERT_NO_FATAL_FAILURE([](auto it) { return ++it; }(cview.rbegin())); ASSERT_NE(view.begin(), view.end()); ASSERT_NE(cview.begin(), cview.end()); ASSERT_NE(view.rbegin(), view.rend()); ASSERT_NE(cview.rbegin(), cview.rend()); ASSERT_EQ(view.size(), 1u); ASSERT_FALSE(view.empty()); registry.emplace(e0); ASSERT_EQ(view.size(), 2u); view.get(e0) = '1'; std::get<0>(view.get(e1)) = '2'; ASSERT_EQ(view.get<0u>(e0), '1'); ASSERT_EQ(cview.get<0u>(e0), view.get(e0)); ASSERT_EQ(view.get(e1), '2'); for(auto entity: view) { ASSERT_TRUE(entity == e0 || entity == e1); ASSERT_TRUE(entity != e0 || cview.get(entity) == '1'); ASSERT_TRUE(entity != e1 || std::get(cview.get(entity)) == '2'); } registry.erase(e0); registry.erase(e1); ASSERT_EQ(view.begin(), view.end()); ASSERT_EQ(view.rbegin(), view.rend()); ASSERT_TRUE(view.empty()); decltype(view) invalid{}; ASSERT_TRUE(view); ASSERT_TRUE(cview); ASSERT_FALSE(invalid); } TEST(SingleComponentView, InvalidView) { entt::basic_view>, entt::exclude_t<>> view{}; ASSERT_FALSE(view); ASSERT_EQ(view.size(), 0u); ASSERT_TRUE(view.empty()); ASSERT_FALSE(view.contains(entt::null)); ASSERT_EQ(view.find(entt::null), view.end()); ASSERT_EQ(view.front(), static_cast(entt::null)); ASSERT_EQ(view.back(), static_cast(entt::null)); ASSERT_EQ(view.begin(), typename decltype(view)::iterator{}); ASSERT_EQ(view.begin(), view.end()); ASSERT_EQ(view.rbegin(), typename decltype(view)::reverse_iterator{}); ASSERT_EQ(view.rbegin(), view.rend()); auto iterable = view.each(); ASSERT_EQ(iterable.begin(), iterable.end()); ASSERT_EQ(iterable.cbegin(), iterable.cend()); view.each([](const int &) { FAIL(); }); view.each([](const entt::entity, const int &) { FAIL(); }); entt::storage storage; view.storage(storage); ASSERT_TRUE(view); view.each([](const int &) { FAIL(); }); view.each([](const entt::entity, const int &) { FAIL(); }); } TEST(SingleComponentView, Constructors) { entt::storage storage{}; entt::view> invalid{}; entt::basic_view from_storage{storage}; entt::basic_view from_tuple{std::forward_as_tuple(storage)}; ASSERT_FALSE(invalid); ASSERT_TRUE(from_storage); ASSERT_TRUE(from_tuple); ASSERT_NE(from_storage.handle(), nullptr); ASSERT_EQ(from_storage.handle(), from_tuple.handle()); } TEST(SingleComponentView, Handle) { entt::registry registry; const auto entity = registry.create(); auto view = registry.view(); auto *handle = view.handle(); ASSERT_NE(handle, nullptr); ASSERT_TRUE(handle->empty()); ASSERT_FALSE(handle->contains(entity)); ASSERT_EQ(handle, view.handle()); registry.emplace(entity); ASSERT_FALSE(handle->empty()); ASSERT_TRUE(handle->contains(entity)); ASSERT_EQ(handle, view.handle()); } TEST(SingleComponentView, LazyTypeFromConstRegistry) { entt::registry registry{}; auto eview = std::as_const(registry).view(); auto cview = std::as_const(registry).view(); const auto entity = registry.create(); registry.emplace(entity); registry.emplace(entity); ASSERT_FALSE(cview); ASSERT_FALSE(eview); ASSERT_TRUE(cview.empty()); ASSERT_EQ(eview.size(), 0u); ASSERT_FALSE(cview.contains(entity)); ASSERT_EQ(cview.begin(), cview.end()); ASSERT_EQ(eview.rbegin(), eview.rend()); ASSERT_EQ(eview.find(entity), eview.end()); ASSERT_NE(cview.front(), entity); ASSERT_NE(eview.back(), entity); } TEST(SingleComponentView, ElementAccess) { entt::registry registry; auto view = registry.view(); auto cview = std::as_const(registry).view(); const auto e0 = registry.create(); registry.emplace(e0, 42); const auto e1 = registry.create(); registry.emplace(e1, 3); for(auto i = 0u; i < view.size(); ++i) { ASSERT_EQ(view[i], i ? e0 : e1); ASSERT_EQ(cview[i], i ? e0 : e1); } ASSERT_EQ(view[e0], 42); ASSERT_EQ(cview[e1], 3); } TEST(SingleComponentView, Contains) { entt::registry registry; const auto e0 = registry.create(); registry.emplace(e0); const auto e1 = registry.create(); registry.emplace(e1); registry.destroy(e0); auto view = registry.view(); ASSERT_FALSE(view.contains(e0)); ASSERT_TRUE(view.contains(e1)); } TEST(SingleComponentView, Empty) { entt::registry registry; auto view = registry.view(); ASSERT_EQ(view.size(), 0u); ASSERT_EQ(view.begin(), view.end()); ASSERT_EQ(view.rbegin(), view.rend()); } TEST(SingleComponentView, Each) { entt::registry registry; entt::entity entity[2]{registry.create(), registry.create()}; auto view = registry.view(entt::exclude); auto cview = std::as_const(registry).view(); registry.emplace(entity[0u], 0); registry.emplace(entity[1u], 1); auto iterable = view.each(); auto citerable = cview.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.base(), view.begin()); ASSERT_EQ((it++, ++it), iterable.end()); ASSERT_EQ(it.base(), view.end()); view.each([expected = 1](auto entt, int &value) mutable { ASSERT_EQ(static_cast(entt::to_integral(entt)), expected); ASSERT_EQ(value, expected); --expected; }); cview.each([expected = 1](const int &value) mutable { ASSERT_EQ(value, 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 int &>); // do not use iterable, make sure an iterable view works when created from a temporary for(auto [entt, value]: view.each()) { ASSERT_EQ(static_cast(entt::to_integral(entt)), value); } } TEST(SingleComponentView, ConstNonConstAndAllInBetween) { entt::registry registry; auto view = registry.view(); auto cview = std::as_const(registry).view(); ASSERT_EQ(view.size(), 0u); ASSERT_EQ(cview.size(), 0u); registry.emplace(registry.create(), 0); ASSERT_EQ(view.size(), 1u); ASSERT_EQ(cview.size(), 1u); static_assert(std::is_same_v({})), int &>); static_assert(std::is_same_v({})), int &>); static_assert(std::is_same_v>); static_assert(std::is_same_v({})), const int &>); static_assert(std::is_same_v({})), const int &>); static_assert(std::is_same_v>); static_assert(std::is_same_v()), decltype(cview)>); view.each([](auto &&i) { static_assert(std::is_same_v); }); cview.each([](auto &&i) { static_assert(std::is_same_v); }); for(auto [entt, iv]: view.each()) { static_assert(std::is_same_v); static_assert(std::is_same_v); } for(auto [entt, iv]: cview.each()) { static_assert(std::is_same_v); static_assert(std::is_same_v); } } TEST(SingleComponentView, ConstNonConstAndAllInBetweenWithEmptyType) { entt::registry registry; auto view = registry.view(); auto cview = std::as_const(registry).view(); ASSERT_EQ(view.size(), 0u); ASSERT_EQ(cview.size(), 0u); registry.emplace(registry.create()); ASSERT_EQ(view.size(), 1u); ASSERT_EQ(cview.size(), 1u); static_assert(std::is_same_v>); static_assert(std::is_same_v>); static_assert(std::is_same_v()), decltype(cview)>); for(auto [entt]: view.each()) { static_assert(std::is_same_v); } for(auto [entt]: cview.each()) { static_assert(std::is_same_v); } } TEST(SingleComponentView, Find) { entt::registry registry; auto view = registry.view(); const auto e0 = registry.create(); registry.emplace(e0); const auto e1 = registry.create(); registry.emplace(e1); const auto e2 = registry.create(); registry.emplace(e2); const auto e3 = registry.create(); registry.emplace(e3); registry.erase(e1); ASSERT_NE(view.find(e0), view.end()); ASSERT_EQ(view.find(e1), view.end()); ASSERT_NE(view.find(e2), view.end()); ASSERT_NE(view.find(e3), view.end()); auto it = view.find(e2); ASSERT_EQ(*it, e2); ASSERT_EQ(*(++it), e3); ASSERT_EQ(*(++it), e0); ASSERT_EQ(++it, view.end()); ASSERT_EQ(++view.find(e0), view.end()); const auto e4 = registry.create(); registry.destroy(e4); const auto e5 = registry.create(); registry.emplace(e5); ASSERT_NE(view.find(e5), view.end()); ASSERT_EQ(view.find(e4), view.end()); } TEST(SingleComponentView, EmptyTypes) { entt::registry registry; entt::entity entity = registry.create(); registry.emplace(entity, 0); registry.emplace(entity); registry.view().each([&](const auto entt) { ASSERT_EQ(entity, entt); }); registry.view().each([check = true]() mutable { ASSERT_TRUE(check); check = false; }); for(auto [entt]: registry.view().each()) { static_assert(std::is_same_v); ASSERT_EQ(entity, entt); } registry.view().each([&](const auto entt, int) { ASSERT_EQ(entity, entt); }); registry.view().each([check = true](int) mutable { ASSERT_TRUE(check); check = false; }); for(auto [entt, iv]: registry.view().each()) { static_assert(std::is_same_v); static_assert(std::is_same_v); ASSERT_EQ(entity, entt); } } TEST(SingleComponentView, FrontBack) { entt::registry registry; auto view = registry.view(); ASSERT_EQ(view.front(), static_cast(entt::null)); ASSERT_EQ(view.back(), static_cast(entt::null)); const auto e0 = registry.create(); registry.emplace(e0); const auto e1 = registry.create(); registry.emplace(e1); ASSERT_EQ(view.front(), e1); ASSERT_EQ(view.back(), e0); } TEST(SingleComponentView, DeductionGuide) { entt::registry registry; entt::storage_type_t istorage; entt::storage_type_t sstorage; static_assert(std::is_same_v>, entt::exclude_t<>>, decltype(entt::basic_view{istorage})>); static_assert(std::is_same_v>, entt::exclude_t<>>, decltype(entt::basic_view{std::as_const(istorage)})>); static_assert(std::is_same_v>, entt::exclude_t<>>, decltype(entt::basic_view{sstorage})>); static_assert(std::is_same_v>, entt::exclude_t<>>, decltype(entt::basic_view{std::forward_as_tuple(istorage), std::make_tuple()})>); static_assert(std::is_same_v>, entt::exclude_t<>>, decltype(entt::basic_view{std::forward_as_tuple(std::as_const(istorage))})>); static_assert(std::is_same_v>, entt::exclude_t<>>, decltype(entt::basic_view{std::forward_as_tuple(sstorage)})>); } TEST(SingleComponentView, IterableViewAlgorithmCompatibility) { entt::registry registry; const auto entity = registry.create(); registry.emplace(entity); const auto view = registry.view(); const auto iterable = view.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(SingleComponentView, StableType) { entt::registry registry; auto view = registry.view(); const auto entity = registry.create(); const auto other = registry.create(); registry.emplace(entity); registry.emplace(other); registry.destroy(entity); ASSERT_EQ(view.size_hint(), 2u); ASSERT_FALSE(view.contains(entity)); ASSERT_TRUE(view.contains(other)); ASSERT_EQ(view.front(), other); ASSERT_EQ(view.back(), other); ASSERT_EQ(*view.begin(), other); ASSERT_EQ(++view.begin(), view.end()); view.each([other](const auto entt, stable_type) { ASSERT_EQ(other, entt); }); view.each([check = true](stable_type) mutable { ASSERT_TRUE(check); check = false; }); for(auto [entt, st]: view.each()) { static_assert(std::is_same_v); static_assert(std::is_same_v); ASSERT_EQ(other, entt); } registry.compact(); ASSERT_EQ(view.size_hint(), 1u); } TEST(SingleComponentView, Storage) { entt::registry registry; const auto entity = registry.create(); auto view = registry.view(); auto cview = registry.view(); static_assert(std::is_same_v *>); 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()), entt::storage_type_t *>); static_assert(std::is_same_v *>); static_assert(std::is_same_v()), const 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_TRUE(view); ASSERT_TRUE(cview); ASSERT_NE(view.storage(), nullptr); ASSERT_NE(cview.storage<0u>(), nullptr); ASSERT_EQ(view.size(), 0u); ASSERT_EQ(cview.size(), 0u); view.storage()->emplace(entity); registry.emplace(entity); ASSERT_EQ(view.size(), 1u); ASSERT_EQ(cview.size(), 1u); ASSERT_TRUE(view.storage()->contains(entity)); ASSERT_TRUE(cview.storage<0u>()->contains(entity)); ASSERT_TRUE((registry.all_of(entity))); view.storage()->erase(entity); ASSERT_EQ(view.size(), 0u); ASSERT_EQ(cview.size(), 1u); ASSERT_FALSE(view.storage<0u>()->contains(entity)); ASSERT_TRUE(cview.storage()->contains(entity)); ASSERT_FALSE((registry.all_of(entity))); view = {}; cview = {}; ASSERT_FALSE(view); ASSERT_FALSE(cview); ASSERT_EQ(view.storage<0u>(), nullptr); ASSERT_EQ(cview.storage(), nullptr); } TEST(SingleComponentView, SwapStorage) { using namespace entt::literals; entt::registry registry; entt::basic_view>, entt::exclude_t<>> view; entt::basic_view>, entt::exclude_t<>> cview; ASSERT_FALSE(view); ASSERT_FALSE(cview); ASSERT_EQ(view.storage<0u>(), nullptr); ASSERT_EQ(cview.storage(), nullptr); const entt::entity entity{42u}; registry.emplace(entity); view.storage(registry.storage()); cview.storage(registry.storage()); ASSERT_TRUE(view); ASSERT_TRUE(cview); ASSERT_NE(view.storage<0u>(), nullptr); ASSERT_NE(cview.storage(), nullptr); ASSERT_EQ(view.size(), 1u); ASSERT_EQ(cview.size(), 1u); ASSERT_TRUE(view.contains(entity)); ASSERT_TRUE(cview.contains(entity)); view.storage(registry.storage("other"_hs)); cview.storage(registry.storage("other"_hs)); ASSERT_TRUE(view.empty()); ASSERT_TRUE(cview.empty()); } TEST(MultiComponentView, Functionalities) { entt::registry registry; auto view = registry.view(); auto cview = std::as_const(registry).view(); const auto e0 = registry.create(); registry.emplace(e0, '1'); const auto e1 = registry.create(); registry.emplace(e1, 42); registry.emplace(e1, '2'); ASSERT_EQ(*view.begin(), e1); ASSERT_EQ(*cview.begin(), e1); ASSERT_EQ(++view.begin(), (view.end())); ASSERT_EQ(++cview.begin(), (cview.end())); ASSERT_NO_FATAL_FAILURE((view.begin()++)); ASSERT_NO_FATAL_FAILURE((++cview.begin())); ASSERT_NE(view.begin(), view.end()); ASSERT_NE(cview.begin(), cview.end()); ASSERT_EQ(view.size_hint(), 1u); for(auto entity: view) { ASSERT_EQ(std::get<0>(cview.get(entity)), 42); ASSERT_EQ(std::get<0>(cview.get<0u, 1u>(entity)), 42); ASSERT_EQ(std::get<1>(view.get(entity)), '2'); ASSERT_EQ(std::get<1>(view.get<0u, 1u>(entity)), '2'); ASSERT_EQ(cview.get(entity), '2'); ASSERT_EQ(cview.get<1u>(entity), '2'); } decltype(view) invalid{}; ASSERT_TRUE(view); ASSERT_TRUE(cview); ASSERT_FALSE(invalid); } TEST(MultiComponentView, InvalidView) { entt::basic_view>, entt::exclude_t>> view{}; ASSERT_FALSE(view); ASSERT_EQ(view.size_hint(), 0u); ASSERT_FALSE(view.contains(entt::null)); ASSERT_EQ(view.find(entt::null), view.end()); ASSERT_EQ(view.front(), static_cast(entt::null)); ASSERT_EQ(view.back(), static_cast(entt::null)); ASSERT_EQ(view.begin(), typename decltype(view)::iterator{}); ASSERT_EQ(view.begin(), view.end()); auto iterable = view.each(); ASSERT_EQ(iterable.begin(), iterable.end()); ASSERT_EQ(iterable.cbegin(), iterable.cend()); view.each([](const int &) { FAIL(); }); view.each([](const entt::entity, const int &) { FAIL(); }); entt::storage storage; view.storage(storage); ASSERT_FALSE(view); view.each([](const int &) { FAIL(); }); view.each([](const entt::entity, const int &) { FAIL(); }); entt::storage other; view.storage(other); ASSERT_TRUE(view); view.each([](const int &) { FAIL(); }); view.each([](const entt::entity, const int &) { FAIL(); }); } TEST(MultiComponentView, Constructors) { entt::storage storage{}; entt::view> invalid{}; entt::basic_view from_storage{storage, storage}; entt::basic_view from_tuple{std::forward_as_tuple(storage, storage)}; ASSERT_FALSE(invalid); ASSERT_TRUE(from_storage); ASSERT_TRUE(from_tuple); ASSERT_NE(from_storage.handle(), nullptr); ASSERT_EQ(from_storage.handle(), from_tuple.handle()); } TEST(MultiComponentView, Handle) { entt::registry registry; const auto entity = registry.create(); auto view = registry.view(); auto *handle = view.handle(); ASSERT_NE(handle, nullptr); ASSERT_TRUE(handle->empty()); ASSERT_FALSE(handle->contains(entity)); ASSERT_EQ(handle, view.handle()); registry.emplace(entity); ASSERT_FALSE(handle->empty()); ASSERT_TRUE(handle->contains(entity)); ASSERT_EQ(handle, view.handle()); view.refresh(); auto *other = view.handle(); ASSERT_NE(other, nullptr); ASSERT_TRUE(other->empty()); ASSERT_FALSE(other->contains(entity)); ASSERT_EQ(other, view.handle()); ASSERT_NE(handle, other); view.use(); ASSERT_NE(other, view.handle()); ASSERT_EQ(handle, view.handle()); } TEST(MultiComponentView, LazyTypesFromConstRegistry) { entt::registry registry{}; auto view = std::as_const(registry).view(); const auto entity = registry.create(); registry.emplace(entity); registry.emplace(entity); ASSERT_FALSE(view); ASSERT_EQ(view.size_hint(), 0u); ASSERT_FALSE(view.contains(entity)); ASSERT_EQ(view.begin(), view.end()); ASSERT_EQ(view.find(entity), view.end()); ASSERT_NE(view.front(), entity); ASSERT_NE(view.back(), entity); } TEST(MultiComponentView, LazyExcludedTypeFromConstRegistry) { entt::registry registry; auto entity = registry.create(); registry.emplace(entity); auto view = std::as_const(registry).view(entt::exclude); ASSERT_FALSE(view); ASSERT_EQ(view.size_hint(), 1u); ASSERT_TRUE(view.contains(entity)); ASSERT_NE(view.begin(), view.end()); ASSERT_NE(view.find(entity), view.end()); ASSERT_EQ(view.front(), entity); ASSERT_EQ(view.back(), entity); } TEST(MultiComponentView, Iterator) { entt::registry registry; const entt::entity entity[2]{registry.create(), registry.create()}; registry.insert(std::begin(entity), std::end(entity)); registry.insert(std::begin(entity), std::end(entity)); const auto view = registry.view(); using iterator = typename decltype(view)::iterator; iterator end{view.begin()}; iterator begin{}; begin = view.end(); std::swap(begin, end); ASSERT_EQ(begin, view.begin()); ASSERT_EQ(end, view.end()); ASSERT_NE(begin, end); ASSERT_EQ(*begin, entity[1u]); ASSERT_EQ(*begin.operator->(), entity[1u]); ASSERT_EQ(begin++, view.begin()); ASSERT_EQ(*begin, entity[0u]); ASSERT_EQ(*begin.operator->(), entity[0u]); ASSERT_EQ(++begin, view.end()); } TEST(MultiComponentView, ElementAccess) { entt::registry registry; auto view = registry.view(); auto cview = std::as_const(registry).view(); const auto e0 = registry.create(); registry.emplace(e0, 42); registry.emplace(e0, '0'); const auto e1 = registry.create(); registry.emplace(e1, 3); registry.emplace(e1, '1'); ASSERT_EQ(view[e0], std::make_tuple(42, '0')); ASSERT_EQ(cview[e1], std::make_tuple(3, '1')); } TEST(MultiComponentView, Contains) { entt::registry registry; 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); auto view = registry.view(); ASSERT_FALSE(view.contains(e0)); ASSERT_TRUE(view.contains(e1)); } TEST(MultiComponentView, SizeHint) { entt::registry registry; const auto e0 = registry.create(); registry.emplace(e0); registry.emplace(e0); const auto e1 = registry.create(); registry.emplace(e1); registry.emplace(e1); auto view = registry.view(); ASSERT_EQ(view.size_hint(), 1u); ASSERT_EQ(view.begin(), view.end()); } TEST(MultiComponentView, Each) { entt::registry registry; entt::entity entity[2]{registry.create(), registry.create()}; auto view = registry.view(entt::exclude); auto cview = std::as_const(registry).view(); registry.emplace(entity[0u], 0); registry.emplace(entity[0u], static_cast(0)); registry.emplace(entity[1u], 1); registry.emplace(entity[1u], static_cast(1)); auto iterable = view.each(); auto citerable = cview.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.base(), view.begin()); ASSERT_EQ((it++, ++it), iterable.end()); ASSERT_EQ(it.base(), view.end()); view.each([expected = 1](auto entt, int &ivalue, char &cvalue) mutable { ASSERT_EQ(static_cast(entt::to_integral(entt)), expected); ASSERT_EQ(ivalue, expected); ASSERT_EQ(cvalue, expected); --expected; }); cview.each([expected = 1](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 view works when created from a temporary for(auto [entt, ivalue, cvalue]: registry.view().each()) { ASSERT_EQ(static_cast(entt::to_integral(entt)), ivalue); ASSERT_EQ(static_cast(entt::to_integral(entt)), cvalue); } } TEST(MultiComponentView, EachWithSuggestedType) { entt::registry registry; auto view = registry.view(); for(auto i = 0; i < 3; ++i) { const auto entity = registry.create(); registry.emplace(entity, i); registry.emplace(entity); } // makes char a better candidate during iterations const auto entity = registry.create(); registry.emplace(entity, 99); view.use(); view.each([value = 2](const auto curr, const auto) mutable { ASSERT_EQ(curr, value--); }); registry.sort([](const auto lhs, const auto rhs) { return lhs < rhs; }); view.use<0u>(); view.each([value = 0](const auto curr, const auto) mutable { ASSERT_EQ(curr, value++); }); registry.sort([](const auto lhs, const auto rhs) { return lhs > rhs; }); auto value = registry.view().size_hint(); for(auto &&curr: registry.view().each()) { ASSERT_EQ(std::get<1>(curr), static_cast(--value)); } registry.sort([](const auto lhs, const auto rhs) { return lhs < rhs; }); value = {}; view.use(); for(auto &&curr: view.each()) { ASSERT_EQ(std::get<1>(curr), static_cast(value++)); } } TEST(MultiComponentView, EachWithHoles) { entt::registry registry; const auto e0 = registry.create(); const auto e1 = registry.create(); const auto e2 = registry.create(); registry.emplace(e0, '0'); registry.emplace(e1, '1'); registry.emplace(e0, 0); registry.emplace(e2, 2); auto view = registry.view(); view.each([e0](auto entity, const char &c, const int &i) { ASSERT_EQ(entity, e0); ASSERT_EQ(c, '0'); ASSERT_EQ(i, 0); }); for(auto &&curr: view.each()) { ASSERT_EQ(std::get<0>(curr), e0); ASSERT_EQ(std::get<1>(curr), '0'); ASSERT_EQ(std::get<2>(curr), 0); } } TEST(MultiComponentView, ConstNonConstAndAllInBetween) { entt::registry registry; auto view = registry.view(); ASSERT_EQ(view.size_hint(), 0u); const auto entity = registry.create(); registry.emplace(entity, 0); registry.emplace(entity); registry.emplace(entity, 'c'); ASSERT_EQ(view.size_hint(), 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({})), 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).view())>); static_assert(std::is_same_v()), decltype(std::as_const(registry).view())>); static_assert(std::is_same_v()), decltype(std::as_const(registry).view())>); view.each([](auto &&i, auto &&c) { static_assert(std::is_same_v); static_assert(std::is_same_v); }); for(auto [entt, iv, cv]: view.each()) { static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); } } TEST(MultiComponentView, Find) { entt::registry registry; auto view = registry.view(); 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(view.find(e0), view.end()); ASSERT_EQ(view.find(e1), view.end()); ASSERT_NE(view.find(e2), view.end()); ASSERT_NE(view.find(e3), view.end()); auto it = view.find(e2); ASSERT_EQ(*it, e2); ASSERT_EQ(*(++it), e3); ASSERT_EQ(*(++it), e0); ASSERT_EQ(++it, view.end()); ASSERT_EQ(++view.find(e0), view.end()); const auto e4 = registry.create(); registry.destroy(e4); const auto e5 = registry.create(); registry.emplace(e5); registry.emplace(e5); ASSERT_NE(view.find(e5), view.end()); ASSERT_EQ(view.find(e4), view.end()); } TEST(MultiComponentView, 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 e2 = registry.create(); registry.emplace(e2, 2); const auto e3 = registry.create(); registry.emplace(e3, 3); registry.emplace(e3); const auto view = std::as_const(registry).view(entt::exclude); for(const auto entity: view) { ASSERT_TRUE(entity == e0 || entity == e2); if(entity == e0) { ASSERT_EQ(view.get(e0), 0); ASSERT_EQ(view.get<0u>(e0), 0); } else if(entity == e2) { ASSERT_EQ(std::get<0>(view.get(e2)), 2); } } registry.emplace(e0); registry.emplace(e2); registry.erase(e1); registry.erase(e3); for(const auto entity: view) { ASSERT_TRUE(entity == e1 || entity == e3); if(entity == e1) { ASSERT_EQ(std::get<0>(view.get(e1)), 1); } else if(entity == e3) { ASSERT_EQ(view.get(e3), 3); ASSERT_EQ(view.get<0u>(e3), 3); } } } TEST(MultiComponentView, EmptyTypes) { entt::registry registry; auto v1 = registry.view(entt::exclude); auto v2 = registry.view(entt::exclude); auto v3 = registry.view(entt::exclude); const auto entity = registry.create(); registry.emplace(entity); registry.emplace(entity); registry.emplace(entity); const auto other = registry.create(); registry.emplace(other); registry.emplace(other); registry.emplace(other); registry.emplace(other); const auto ignored = registry.create(); registry.emplace(ignored); registry.emplace(ignored); v1.each([entity](const auto entt, int, char) { ASSERT_EQ(entity, entt); }); for(auto [entt, iv, cv]: v1.each()) { static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); ASSERT_EQ(entity, entt); } v2.each([check = true](int, char) mutable { ASSERT_TRUE(check); check = false; }); for(auto [entt, iv, cv]: v2.each()) { static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); ASSERT_EQ(entity, entt); } v3.each([entity](const auto entt, int, char) { ASSERT_EQ(entity, entt); }); for(auto [entt, iv, cv]: v3.each()) { static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); ASSERT_EQ(entity, entt); } v3.use(); v3.each([entity](const auto entt, int, char) { ASSERT_EQ(entity, entt); }); v3.use<0u>(); for(auto [entt, iv, cv]: v3.each()) { static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); ASSERT_EQ(entity, entt); } v2.use<1u>(); v2.each([check = true](int, char) mutable { ASSERT_TRUE(check); check = false; }); v2.use(); for(auto [entt, iv, cv]: v2.each()) { static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); ASSERT_EQ(entity, entt); } } TEST(MultiComponentView, FrontBack) { entt::registry registry; auto view = registry.view(); ASSERT_EQ(view.front(), static_cast(entt::null)); ASSERT_EQ(view.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(view.front(), e1); ASSERT_EQ(view.back(), e0); } TEST(MultiComponentView, ExtendedGet) { using type = decltype(std::declval().view().get({})); static_assert(std::tuple_size_v == 2u); static_assert(std::is_same_v, int &>); static_assert(std::is_same_v, char &>); } TEST(MultiComponentView, DeductionGuide) { entt::registry registry; entt::storage_type_t istorage; entt::storage_type_t dstorage; entt::storage_type_t sstorage; static_assert(std::is_same_v, entt::storage_type_t>, entt::exclude_t<>>, decltype(entt::basic_view{istorage, dstorage})>); static_assert(std::is_same_v, entt::storage_type_t>, entt::exclude_t<>>, decltype(entt::basic_view{std::as_const(istorage), dstorage})>); static_assert(std::is_same_v, const entt::storage_type_t>, entt::exclude_t<>>, decltype(entt::basic_view{istorage, std::as_const(dstorage)})>); static_assert(std::is_same_v, const entt::storage_type_t>, entt::exclude_t<>>, decltype(entt::basic_view{std::as_const(istorage), std::as_const(dstorage)})>); static_assert(std::is_same_v, entt::storage_type_t>, entt::exclude_t<>>, decltype(entt::basic_view{istorage, sstorage})>); static_assert(std::is_same_v, entt::storage_type_t>, entt::exclude_t<>>, decltype(entt::basic_view{std::forward_as_tuple(istorage, dstorage), std::make_tuple()})>); static_assert(std::is_same_v, entt::storage_type_t>, entt::exclude_t<>>, decltype(entt::basic_view{std::forward_as_tuple(std::as_const(istorage), dstorage)})>); static_assert(std::is_same_v, const entt::storage_type_t>, entt::exclude_t<>>, decltype(entt::basic_view{std::forward_as_tuple(istorage, std::as_const(dstorage))})>); static_assert(std::is_same_v, const entt::storage_type_t>, entt::exclude_t<>>, decltype(entt::basic_view{std::forward_as_tuple(std::as_const(istorage), std::as_const(dstorage))})>); static_assert(std::is_same_v, entt::storage_type_t>, entt::exclude_t<>>, decltype(entt::basic_view{std::forward_as_tuple(istorage, sstorage)})>); static_assert(std::is_same_v>, entt::exclude_t>>, decltype(entt::basic_view{std::forward_as_tuple(istorage), std::forward_as_tuple(dstorage)})>); static_assert(std::is_same_v>, entt::exclude_t>>, decltype(entt::basic_view{std::forward_as_tuple(std::as_const(istorage)), std::forward_as_tuple(dstorage)})>); static_assert(std::is_same_v>, entt::exclude_t>>, decltype(entt::basic_view{std::forward_as_tuple(istorage), std::forward_as_tuple(std::as_const(dstorage))})>); static_assert(std::is_same_v>, entt::exclude_t>>, decltype(entt::basic_view{std::forward_as_tuple(std::as_const(istorage)), std::forward_as_tuple(std::as_const(dstorage))})>); static_assert(std::is_same_v>, entt::exclude_t>>, decltype(entt::basic_view{std::forward_as_tuple(istorage), std::forward_as_tuple(sstorage)})>); } TEST(MultiComponentView, IterableViewAlgorithmCompatibility) { entt::registry registry; const auto entity = registry.create(); registry.emplace(entity); registry.emplace(entity); const auto view = registry.view(); const auto iterable = view.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(MultiComponentView, StableType) { entt::registry registry; auto view = registry.view(); const auto entity = registry.create(); const auto other = registry.create(); registry.emplace(entity); registry.emplace(other); registry.emplace(entity); registry.emplace(other); registry.destroy(entity); ASSERT_EQ(view.size_hint(), 1u); view.use(); ASSERT_EQ(view.size_hint(), 2u); ASSERT_FALSE(view.contains(entity)); ASSERT_TRUE(view.contains(other)); ASSERT_EQ(view.front(), other); ASSERT_EQ(view.back(), other); ASSERT_EQ(*view.begin(), other); ASSERT_EQ(++view.begin(), view.end()); view.each([other](const auto entt, int, stable_type) { ASSERT_EQ(other, entt); }); view.each([check = true](int, stable_type) mutable { ASSERT_TRUE(check); check = false; }); for(auto [entt, iv, st]: view.each()) { static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); ASSERT_EQ(other, entt); } registry.compact(); ASSERT_EQ(view.size_hint(), 1u); } TEST(MultiComponentView, StableTypeWithExcludedComponent) { entt::registry registry; auto view = registry.view(entt::exclude); view.use(); const auto entity = registry.create(); const auto other = registry.create(); registry.emplace(entity, 0); registry.emplace(other, 42); registry.emplace(entity); ASSERT_EQ(view.size_hint(), 2u); ASSERT_FALSE(view.contains(entity)); ASSERT_TRUE(view.contains(other)); registry.destroy(entity); ASSERT_EQ(view.size_hint(), 2u); ASSERT_FALSE(view.contains(entity)); ASSERT_TRUE(view.contains(other)); for(auto entt: view) { constexpr entt::entity tombstone = entt::tombstone; ASSERT_NE(entt, tombstone); ASSERT_EQ(entt, other); } for(auto [entt, comp]: view.each()) { constexpr entt::entity tombstone = entt::tombstone; ASSERT_NE(entt, tombstone); ASSERT_EQ(entt, other); ASSERT_EQ(comp.value, 42); } view.each([other](const auto entt, auto &&...) { constexpr entt::entity tombstone = entt::tombstone; ASSERT_NE(entt, tombstone); ASSERT_EQ(entt, other); }); } TEST(MultiComponentView, SameComponentTypes) { entt::registry registry; entt::storage_type_t storage; entt::storage_type_t other; entt::basic_view view{storage, other}; storage.bind(entt::forward_as_any(registry)); other.bind(entt::forward_as_any(registry)); const entt::entity e0{42u}; const entt::entity e1{3u}; storage.emplace(e0, 7); other.emplace(e0, 9); other.emplace(e1, 1); ASSERT_TRUE(view.contains(e0)); ASSERT_FALSE(view.contains(e1)); ASSERT_EQ((view.get<0u, 1u>(e0)), (std::make_tuple(7, 9))); ASSERT_EQ(view.get<1u>(e0), 9); for(auto entt: view) { ASSERT_EQ(entt, e0); } view.each([&](auto entt, auto &&first, auto &&second) { ASSERT_EQ(entt, e0); ASSERT_EQ(first, 7); ASSERT_EQ(second, 9); }); for(auto [entt, first, second]: view.each()) { ASSERT_EQ(entt, e0); ASSERT_EQ(first, 7); ASSERT_EQ(second, 9); } ASSERT_EQ(view.handle(), &storage); view.use<1u>(); ASSERT_EQ(view.handle(), &other); } TEST(MultiComponentView, Storage) { entt::registry registry; const auto entity = registry.create(); auto view = registry.view(entt::exclude); 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()), 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 *>); static_assert(std::is_same_v()), const entt::storage_type_t *>); 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()), 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 *>); static_assert(std::is_same_v()), const entt::storage_type_t *>); ASSERT_TRUE(view); ASSERT_NE(view.storage(), nullptr); ASSERT_NE(view.storage<1u>(), nullptr); ASSERT_NE(view.storage(), nullptr); ASSERT_NE(view.storage<3u>(), nullptr); ASSERT_EQ(view.size_hint(), 0u); view.storage()->emplace(entity); view.storage()->emplace(entity); registry.emplace(entity); registry.emplace(entity); ASSERT_EQ(view.size_hint(), 1u); ASSERT_EQ(view.begin(), view.end()); ASSERT_TRUE(view.storage()->contains(entity)); ASSERT_TRUE(view.storage()->contains(entity)); ASSERT_TRUE(view.storage()->contains(entity)); ASSERT_TRUE(view.storage()->contains(entity)); ASSERT_TRUE((registry.all_of(entity))); view.storage()->erase(entity); registry.erase(entity); ASSERT_EQ(view.size_hint(), 1u); ASSERT_NE(view.begin(), view.end()); ASSERT_TRUE(view.storage()->contains(entity)); ASSERT_TRUE(view.storage()->contains(entity)); ASSERT_FALSE(view.storage()->contains(entity)); ASSERT_FALSE(view.storage()->contains(entity)); ASSERT_TRUE((registry.all_of(entity))); ASSERT_FALSE((registry.any_of(entity))); view.storage<0u>()->erase(entity); ASSERT_EQ(view.size_hint(), 0u); ASSERT_EQ(view.begin(), view.end()); ASSERT_FALSE(view.storage<0u>()->contains(entity)); ASSERT_TRUE(view.storage<1u>()->contains(entity)); ASSERT_FALSE(view.storage<2u>()->contains(entity)); ASSERT_FALSE(view.storage<3u>()->contains(entity)); ASSERT_TRUE((registry.all_of(entity))); ASSERT_FALSE((registry.any_of(entity))); view = {}; ASSERT_FALSE(view); ASSERT_EQ(view.storage<0u>(), nullptr); ASSERT_EQ(view.storage(), nullptr); ASSERT_EQ(view.storage<2u>(), nullptr); ASSERT_EQ(view.storage(), nullptr); } TEST(MultiComponentView, SwapStorage) { using namespace entt::literals; entt::registry registry; entt::basic_view>, entt::exclude_t>> view; ASSERT_FALSE(view); ASSERT_EQ(view.storage<0u>(), nullptr); ASSERT_EQ(view.storage(), nullptr); const entt::entity entity{42u}; registry.emplace(entity); registry.emplace(entity); view.storage(registry.storage()); view.storage<1u>(registry.storage()); ASSERT_TRUE(view); ASSERT_NE(view.storage(), nullptr); ASSERT_NE(view.storage<1u>(), nullptr); ASSERT_EQ(view.size_hint(), 1u); ASSERT_FALSE(view.contains(entity)); view.storage(registry.storage("other"_hs)); ASSERT_EQ(view.size_hint(), 1u); ASSERT_TRUE(view.contains(entity)); view.storage(registry.storage("empty"_hs)); ASSERT_EQ(view.size_hint(), 0u); } TEST(View, Pipe) { entt::registry registry; const auto entity = registry.create(); const auto other = registry.create(); registry.emplace(entity); registry.emplace(entity); registry.emplace(entity); registry.emplace(entity); registry.emplace(other); registry.emplace(other); registry.emplace(other); auto view1 = registry.view(entt::exclude); auto view2 = registry.view(entt::exclude); auto view3 = registry.view(); auto view4 = registry.view(); static_assert(std::is_same_v, const entt::storage_type_t>, entt::exclude_t, entt::storage_type_t>>, decltype(view1 | view2)>); static_assert(std::is_same_v, entt::storage_type_t>, entt::exclude_t, const entt::storage_type_t>>, decltype(view2 | view1)>); static_assert(std::is_same_v); ASSERT_FALSE((view1 | view2).contains(entity)); ASSERT_TRUE((view1 | view2).contains(other)); ASSERT_TRUE((view3 | view2).contains(entity)); ASSERT_FALSE((view3 | view2).contains(other)); ASSERT_FALSE((view1 | view2 | view3).contains(entity)); ASSERT_FALSE((view1 | view2 | view3).contains(other)); ASSERT_FALSE((view1 | view4 | view2).contains(entity)); ASSERT_TRUE((view1 | view4 | view2).contains(other)); view1 = {}; view3 = {}; ASSERT_FALSE(view1); ASSERT_TRUE(view2); ASSERT_FALSE(view3); ASSERT_TRUE(view4); auto pack14 = view1 | view4; auto pack32 = view3 | view2; ASSERT_FALSE(pack14); ASSERT_FALSE(pack32); ASSERT_EQ(pack14.storage(), nullptr); ASSERT_EQ(pack14.storage(), nullptr); ASSERT_NE(pack14.storage(), nullptr); ASSERT_EQ(pack32.storage(), nullptr); ASSERT_NE(pack32.storage(), nullptr); ASSERT_NE(pack32.storage(), nullptr); }