From b7c819bf48e983db8e9cfd50fb01789f7e82261f Mon Sep 17 00:00:00 2001 From: Michele Caini Date: Tue, 24 Jan 2023 16:46:50 +0100 Subject: [PATCH] test: entity storage --- test/entt/entity/storage_entity.cpp | 430 ++++++++++++++++++++++++++++ 1 file changed, 430 insertions(+) create mode 100644 test/entt/entity/storage_entity.cpp diff --git a/test/entt/entity/storage_entity.cpp b/test/entt/entity/storage_entity.cpp new file mode 100644 index 000000000..ca14d8ea3 --- /dev/null +++ b/test/entt/entity/storage_entity.cpp @@ -0,0 +1,430 @@ +#include +#include +#include +#include +#include "../common/config.h" +#include "../common/throwing_allocator.hpp" + +TEST(StorageEntity, TypeAndPolicy) { + entt::storage pool; + + ASSERT_EQ(pool.type(), entt::type_id()); + ASSERT_EQ(pool.policy(), entt::deletion_policy::swap_and_pop); +} + +TEST(StorageEntity, Functionalities) { + entt::entity entities[2u]{entt::entity{0}, entt::entity{1}}; + entt::storage pool; + + ASSERT_TRUE(pool.empty()); + ASSERT_EQ(pool.size(), 0u); + ASSERT_EQ(pool.in_use(), 0u); + + ASSERT_EQ(*pool.push(entt::null), entities[0u]); + ASSERT_EQ(*pool.push(entt::tombstone), entities[1u]); + + ASSERT_FALSE(pool.empty()); + ASSERT_EQ(pool.size(), 2u); + ASSERT_EQ(pool.in_use(), 2u); + + pool.in_use(1u); + + ASSERT_FALSE(pool.empty()); + ASSERT_EQ(pool.size(), 2u); + ASSERT_EQ(pool.in_use(), 1u); + + pool.erase(entities[0u]); + + ASSERT_FALSE(pool.empty()); + ASSERT_EQ(pool.size(), 2u); + ASSERT_EQ(pool.in_use(), 0u); +} + +TEST(StorageEntity, Move) { + using traits_type = entt::entt_traits; + + entt::storage pool; + + pool.push(entt::entity{1}); + + ASSERT_EQ(pool.size(), 2u); + ASSERT_EQ(pool.in_use(), 1u); + + ASSERT_TRUE(std::is_move_constructible_v); + ASSERT_TRUE(std::is_move_assignable_v); + + entt::storage other{std::move(pool)}; + + ASSERT_EQ(pool.size(), 0u); + ASSERT_EQ(other.size(), 2u); + ASSERT_EQ(pool.in_use(), 0u); + ASSERT_EQ(other.in_use(), 1u); + ASSERT_EQ(pool.at(0u), static_cast(entt::null)); + ASSERT_EQ(other.at(0u), entt::entity{1}); + + pool = std::move(other); + + ASSERT_EQ(pool.size(), 2u); + ASSERT_EQ(other.size(), 0u); + ASSERT_EQ(pool.in_use(), 1u); + ASSERT_EQ(other.in_use(), 0u); + ASSERT_EQ(pool.at(0u), entt::entity{1}); + ASSERT_EQ(other.at(0u), static_cast(entt::null)); + + other = entt::storage{}; + + other.push(entt::entity{3}); + other = std::move(pool); + + ASSERT_EQ(pool.size(), 0u); + ASSERT_EQ(other.size(), 2u); + ASSERT_EQ(pool.in_use(), 0u); + ASSERT_EQ(other.in_use(), 1u); + ASSERT_EQ(pool.at(0u), static_cast(entt::null)); + ASSERT_EQ(other.at(0u), entt::entity{1}); + + other.clear(); + + ASSERT_EQ(other.size(), 2u); + ASSERT_EQ(other.in_use(), 0u); + + ASSERT_EQ(*other.push(entt::null), traits_type::construct(1, 1)); + ASSERT_EQ(*other.push(entt::null), entt::entity{0}); + ASSERT_EQ(*other.push(entt::null), entt::entity{2}); +} + +TEST(StorageEntity, Swap) { + using traits_type = entt::entt_traits; + + entt::storage pool; + entt::storage other; + + pool.push(entt::entity{1}); + + other.push(entt::entity{2}); + other.push(entt::entity{0}); + other.erase(entt::entity{2}); + + ASSERT_EQ(pool.size(), 2u); + ASSERT_EQ(other.size(), 3u); + ASSERT_EQ(pool.in_use(), 1u); + ASSERT_EQ(other.in_use(), 1u); + + pool.swap(other); + + ASSERT_EQ(pool.size(), 3u); + ASSERT_EQ(other.size(), 2u); + ASSERT_EQ(pool.in_use(), 1u); + ASSERT_EQ(other.in_use(), 1u); + + ASSERT_EQ(pool.at(0u), entt::entity{0}); + ASSERT_EQ(other.at(0u), entt::entity{1}); + + pool.clear(); + other.clear(); + + ASSERT_EQ(pool.size(), 3u); + ASSERT_EQ(other.size(), 2u); + ASSERT_EQ(pool.in_use(), 0u); + ASSERT_EQ(other.in_use(), 0u); + + ASSERT_EQ(*other.push(entt::null), traits_type::construct(1, 1)); + ASSERT_EQ(*other.push(entt::null), entt::entity{0}); + ASSERT_EQ(*other.push(entt::null), entt::entity{2}); +} + +TEST(StorageEntity, Push) { + using traits_type = entt::entt_traits; + + entt::storage pool; + + ASSERT_EQ(*pool.push(entt::null), entt::entity{0}); + ASSERT_EQ(*pool.push(entt::tombstone), entt::entity{1}); + ASSERT_EQ(*pool.push(entt::entity{0}), entt::entity{2}); + ASSERT_EQ(*pool.push(traits_type::construct(1, 1)), entt::entity{3}); + ASSERT_EQ(*pool.push(traits_type::construct(5, 3)), traits_type::construct(5, 3)); + + ASSERT_LT(pool.index(entt::entity{0}), pool.in_use()); + ASSERT_LT(pool.index(entt::entity{1}), pool.in_use()); + ASSERT_LT(pool.index(entt::entity{2}), pool.in_use()); + ASSERT_LT(pool.index(entt::entity{3}), pool.in_use()); + ASSERT_GE(pool.index(entt::entity{4}), pool.in_use()); + ASSERT_LT(pool.index(traits_type::construct(5, 3)), pool.in_use()); + + ASSERT_EQ(*pool.push(traits_type::construct(4, 42)), traits_type::construct(4, 42)); + ASSERT_EQ(*pool.push(traits_type::construct(4, 43)), entt::entity{6}); + + entt::entity entities[2u]{entt::entity{1}, traits_type::construct(5, 3)}; + + pool.erase(entities, entities + 2u); + pool.erase(entt::entity{2}); + + ASSERT_EQ(pool.current(entities[0u]), 1); + ASSERT_EQ(pool.current(entities[1u]), 4); + ASSERT_EQ(pool.current(entt::entity{2}), 1); + + ASSERT_LT(pool.index(entt::entity{0}), pool.in_use()); + ASSERT_GE(pool.index(traits_type::construct(1, 1)), pool.in_use()); + ASSERT_GE(pool.index(traits_type::construct(2, 1)), pool.in_use()); + ASSERT_LT(pool.index(entt::entity{3}), pool.in_use()); + ASSERT_LT(pool.index(traits_type::construct(4, 42)), pool.in_use()); + ASSERT_GE(pool.index(traits_type::construct(5, 4)), pool.in_use()); + + ASSERT_EQ(*pool.push(entt::null), traits_type::construct(2, 1)); + ASSERT_EQ(*pool.push(traits_type::construct(1, 3)), traits_type::construct(1, 3)); + ASSERT_EQ(*pool.push(entt::null), traits_type::construct(5, 4)); + ASSERT_EQ(*pool.push(entt::null), entt::entity{7}); +} + +TEST(StorageEntity, Spawn) { + using traits_type = entt::entt_traits; + + entt::storage pool; + entt::entity entities[2u]{}; + + ASSERT_EQ(pool.spawn(), entt::entity{0}); + ASSERT_EQ(pool.spawn(entt::null), entt::entity{1}); + ASSERT_EQ(pool.spawn(entt::tombstone), entt::entity{2}); + ASSERT_EQ(pool.spawn(entt::entity{0}), entt::entity{3}); + ASSERT_EQ(pool.spawn(traits_type::construct(1, 1)), entt::entity{4}); + ASSERT_EQ(pool.spawn(traits_type::construct(6, 3)), traits_type::construct(6, 3)); + + ASSERT_LT(pool.index(entt::entity{0}), pool.in_use()); + ASSERT_LT(pool.index(entt::entity{1}), pool.in_use()); + ASSERT_LT(pool.index(entt::entity{2}), pool.in_use()); + ASSERT_LT(pool.index(entt::entity{3}), pool.in_use()); + ASSERT_LT(pool.index(entt::entity{4}), pool.in_use()); + ASSERT_GE(pool.index(entt::entity{5}), pool.in_use()); + ASSERT_LT(pool.index(traits_type::construct(6, 3)), pool.in_use()); + + ASSERT_EQ(pool.spawn(traits_type::construct(5, 42)), traits_type::construct(5, 42)); + ASSERT_EQ(pool.spawn(traits_type::construct(5, 43)), entt::entity{7}); + + pool.erase(entt::entity{2}); + + ASSERT_EQ(pool.spawn(), traits_type::construct(2, 1)); + + pool.erase(traits_type::construct(2, 1)); + pool.spawn(entities, entities + 2u); + + ASSERT_EQ(entities[0u], traits_type::construct(2, 2)); + ASSERT_EQ(entities[1u], entt::entity{8}); +} + +TEST(StorageEntity, Pack) { + entt::storage pool; + entt::entity entities[3u]{entt::entity{1}, entt::entity{3}, entt::entity{42}}; + + pool.push(entities, entities + 3u); + std::swap(entities[0u], entities[1u]); + + const auto len = pool.pack(entities + 1u, entities + 3u); + auto it = pool.each().cbegin().base(); + + ASSERT_NE(it, pool.cbegin()); + ASSERT_NE(it, pool.cend()); + + ASSERT_EQ(len, 2u); + ASSERT_NE(it + len, pool.cend()); + ASSERT_EQ(it + len + 1u, pool.cend()); + + ASSERT_EQ(*it++, entities[1u]); + ASSERT_EQ(*it++, entities[2u]); + + ASSERT_NE(it, pool.cend()); + ASSERT_EQ(*it++, entities[0u]); + ASSERT_EQ(it, pool.cend()); +} + +TEST(StorageEntity, Iterable) { + using iterator = typename entt::storage::iterable::iterator; + + static_assert(std::is_same_v>); + static_assert(std::is_same_v>>); + static_assert(std::is_same_v); + + entt::storage pool; + + pool.spawn(entt::entity{1}); + pool.spawn(entt::entity{3}); + pool.spawn(entt::entity{42}); + + pool.erase(entt::entity{3}); + + auto iterable = pool.each(); + + iterator end{iterable.begin()}; + iterator begin{}; + begin = iterable.end(); + std::swap(begin, end); + + ASSERT_EQ(begin, iterable.begin()); + ASSERT_EQ(end, iterable.end()); + ASSERT_NE(begin, end); + + ASSERT_NE(begin.base(), pool.begin()); + ASSERT_EQ(begin.base(), pool.end() - pool.in_use()); + ASSERT_EQ(end.base(), pool.end()); + + ASSERT_EQ(std::get<0>(*begin.operator->().operator->()), entt::entity{42}); + ASSERT_EQ(std::get<0>(*begin), entt::entity{42}); + + ASSERT_EQ(begin++, iterable.begin()); + ASSERT_EQ(begin.base(), pool.end() - 1); + ASSERT_EQ(++begin, iterable.end()); + ASSERT_EQ(begin.base(), pool.end()); + + for(auto [entity]: iterable) { + static_assert(std::is_same_v); + ASSERT_TRUE(entity != entt::entity{3}); + } +} + +TEST(StorageEntity, ConstIterable) { + using iterator = typename entt::storage::const_iterable::iterator; + + static_assert(std::is_same_v>); + static_assert(std::is_same_v>>); + static_assert(std::is_same_v); + + entt::storage pool; + + pool.spawn(entt::entity{1}); + pool.spawn(entt::entity{3}); + pool.spawn(entt::entity{42}); + + pool.erase(entt::entity{3}); + + auto iterable = std::as_const(pool).each(); + + iterator end{iterable.cbegin()}; + iterator begin{}; + begin = iterable.cend(); + std::swap(begin, end); + + ASSERT_EQ(begin, iterable.cbegin()); + ASSERT_EQ(end, iterable.cend()); + ASSERT_NE(begin, end); + + ASSERT_NE(begin.base(), pool.begin()); + ASSERT_EQ(begin.base(), pool.end() - pool.in_use()); + ASSERT_EQ(end.base(), pool.end()); + + ASSERT_EQ(std::get<0>(*begin.operator->().operator->()), entt::entity{42}); + ASSERT_EQ(std::get<0>(*begin), entt::entity{42}); + + ASSERT_EQ(begin++, iterable.begin()); + ASSERT_EQ(begin.base(), pool.end() - 1); + ASSERT_EQ(++begin, iterable.end()); + ASSERT_EQ(begin.base(), pool.end()); + + for(auto [entity]: iterable) { + static_assert(std::is_same_v); + ASSERT_TRUE(entity != entt::entity{3}); + } +} + +ENTT_DEBUG_TEST(StorageEntity, SwapElements) { + entt::storage pool; + + pool.push(entt::entity{0}); + pool.push(entt::entity{1}); + + ASSERT_EQ(pool.size(), 2u); + ASSERT_EQ(pool.in_use(), 2u); + ASSERT_TRUE(pool.contains(entt::entity{0})); + ASSERT_TRUE(pool.contains(entt::entity{1})); + + ASSERT_EQ(*pool.begin(), entt::entity{1}); + ASSERT_EQ(*++pool.begin(), entt::entity{0}); + + pool.swap_elements(entt::entity{0}, entt::entity{1}); + + ASSERT_EQ(*pool.begin(), entt::entity{0}); + ASSERT_EQ(*++pool.begin(), entt::entity{1}); +} + +ENTT_DEBUG_TEST(StorageEntityDeathTest, SwapElements) { + entt::storage pool; + + pool.push(entt::entity{1}); + + ASSERT_EQ(pool.size(), 2u); + ASSERT_EQ(pool.in_use(), 1u); + ASSERT_TRUE(pool.contains(entt::entity{0})); + ASSERT_TRUE(pool.contains(entt::entity{1})); + + ASSERT_DEATH(pool.swap_elements(entt::entity{0}, entt::entity{1}), ""); +} + +ENTT_DEBUG_TEST(StorageEntityDeathTest, InUse) { + entt::storage pool; + + pool.push(entt::entity{0}); + pool.push(entt::entity{1}); + + ASSERT_DEATH(pool.in_use(3u), ""); +} + +ENTT_DEBUG_TEST(StorageEntityDeathTest, SortAndRespect) { + entt::storage pool; + entt::storage other; + + pool.push(entt::entity{1}); + pool.push(entt::entity{2}); + pool.erase(entt::entity{2}); + + other.push(entt::entity{2}); + + ASSERT_DEATH(pool.sort([&pool](auto...) { return true; }), ""); + ASSERT_DEATH(pool.respect(other), ""); +} + +TEST(StorageEntity, CustomAllocator) { + test::throwing_allocator allocator{}; + entt::basic_storage> pool{allocator}; + + pool.reserve(1u); + + ASSERT_EQ(pool.size(), 0u); + ASSERT_EQ(pool.in_use(), 0u); + + pool.push(entt::entity{0}); + pool.push(entt::entity{1}); + + ASSERT_EQ(pool.size(), 2u); + ASSERT_EQ(pool.in_use(), 2u); + + decltype(pool) other{std::move(pool), allocator}; + + ASSERT_TRUE(pool.empty()); + ASSERT_FALSE(other.empty()); + ASSERT_EQ(pool.size(), 0u); + ASSERT_EQ(other.size(), 2u); + ASSERT_EQ(pool.in_use(), 0u); + ASSERT_EQ(other.in_use(), 2u); + + pool = std::move(other); + + ASSERT_FALSE(pool.empty()); + ASSERT_TRUE(other.empty()); + ASSERT_EQ(pool.size(), 2u); + ASSERT_EQ(other.size(), 0u); + ASSERT_EQ(pool.in_use(), 2u); + ASSERT_EQ(other.in_use(), 0u); + + pool.swap(other); + pool = std::move(other); + + ASSERT_FALSE(pool.empty()); + ASSERT_TRUE(other.empty()); + ASSERT_EQ(pool.size(), 2u); + ASSERT_EQ(other.size(), 0u); + ASSERT_EQ(pool.in_use(), 2u); + ASSERT_EQ(other.in_use(), 0u); + + pool.clear(); + + ASSERT_EQ(pool.size(), 2u); + ASSERT_EQ(pool.in_use(), 0u); +}