From 58d392e8138f9502c2d51df6a3546f37512e0e97 Mon Sep 17 00:00:00 2001 From: Michele Caini Date: Tue, 13 Dec 2022 15:36:49 +0100 Subject: [PATCH] -: minor changes --- src/entt/entity/registry.hpp | 1 + src/entt/entity/storage.hpp | 1 - src/entt/entity/view.hpp | 2 - test/CMakeLists.txt | 2 +- test/entt/entity/sigh_mixin.cpp | 614 ++++++++++++++++++++++++++++++++ 5 files changed, 616 insertions(+), 4 deletions(-) create mode 100644 test/entt/entity/sigh_mixin.cpp diff --git a/src/entt/entity/registry.hpp b/src/entt/entity/registry.hpp index 176aa7f0c..9e0f793ff 100644 --- a/src/entt/entity/registry.hpp +++ b/src/entt/entity/registry.hpp @@ -25,6 +25,7 @@ #include "group.hpp" #include "sparse_set.hpp" #include "storage.hpp" +#include "storage_mixin.hpp" #include "view.hpp" namespace entt { diff --git a/src/entt/entity/storage.hpp b/src/entt/entity/storage.hpp index 3f1fe3ed8..f7f2e23ed 100644 --- a/src/entt/entity/storage.hpp +++ b/src/entt/entity/storage.hpp @@ -16,7 +16,6 @@ #include "entity.hpp" #include "fwd.hpp" #include "sparse_set.hpp" -#include "storage_mixin.hpp" namespace entt { diff --git a/src/entt/entity/view.hpp b/src/entt/entity/view.hpp index d4d8a39f9..3ff3f089d 100644 --- a/src/entt/entity/view.hpp +++ b/src/entt/entity/view.hpp @@ -11,8 +11,6 @@ #include "../core/type_traits.hpp" #include "entity.hpp" #include "fwd.hpp" -#include "sparse_set.hpp" -#include "storage.hpp" namespace entt { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a9c39f2f3..82512c318 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -228,10 +228,10 @@ SETUP_BASIC_TEST(observer entt/entity/observer.cpp) SETUP_BASIC_TEST(organizer entt/entity/organizer.cpp) SETUP_BASIC_TEST(registry entt/entity/registry.cpp) SETUP_BASIC_TEST(runtime_view entt/entity/runtime_view.cpp) +SETUP_BASIC_TEST(sigh_mixin entt/entity/sigh_mixin.cpp) SETUP_BASIC_TEST(snapshot entt/entity/snapshot.cpp) SETUP_BASIC_TEST(sparse_set entt/entity/sparse_set.cpp) SETUP_BASIC_TEST(storage entt/entity/storage.cpp) -SETUP_BASIC_TEST(storage_mixin entt/entity/storage_mixin.cpp) SETUP_BASIC_TEST(view entt/entity/view.cpp) # Test graph diff --git a/test/entt/entity/sigh_mixin.cpp b/test/entt/entity/sigh_mixin.cpp new file mode 100644 index 000000000..952c650b3 --- /dev/null +++ b/test/entt/entity/sigh_mixin.cpp @@ -0,0 +1,614 @@ +#include +#include +#include +#include +#include +#include "../common/throwing_allocator.hpp" +#include "../common/throwing_type.hpp" + +struct empty_type {}; + +struct stable_type { + static constexpr auto in_place_delete = true; + int value{}; +}; + +struct non_default_constructible { + non_default_constructible() = delete; + + non_default_constructible(int v) + : value{v} {} + + int value{}; +}; + +struct counter { + int value{}; +}; + +template +void listener(counter &counter, Registry &, typename Registry::entity_type) { + ++counter.value; +} + +TEST(SighMixin, GenericType) { + entt::entity entities[2u]{entt::entity{3}, entt::entity{42}}; + entt::sigh_mixin> pool; + entt::sparse_set &base = pool; + entt::registry registry; + + counter on_construct{}; + counter on_destroy{}; + + pool.bind(entt::forward_as_any(registry)); + pool.on_construct().connect<&listener>(on_construct); + pool.on_destroy().connect<&listener>(on_destroy); + + ASSERT_NE(base.emplace(entities[0u]), base.end()); + + pool.emplace(entities[1u]); + + ASSERT_EQ(on_construct.value, 2); + ASSERT_EQ(on_destroy.value, 0); + ASSERT_FALSE(pool.empty()); + + ASSERT_EQ(pool.get(entities[0u]), 0); + ASSERT_EQ(pool.get(entities[1u]), 0); + + base.erase(entities[0u]); + pool.erase(entities[1u]); + + ASSERT_EQ(on_construct.value, 2); + ASSERT_EQ(on_destroy.value, 2); + ASSERT_TRUE(pool.empty()); + + ASSERT_NE(base.insert(std::begin(entities), std::end(entities)), base.end()); + + ASSERT_EQ(pool.get(entities[0u]), 0); + ASSERT_EQ(pool.get(entities[1u]), 0); + ASSERT_FALSE(pool.empty()); + + base.erase(entities[1u]); + + ASSERT_EQ(on_construct.value, 4); + ASSERT_EQ(on_destroy.value, 3); + ASSERT_FALSE(pool.empty()); + + base.erase(entities[0u]); + + ASSERT_EQ(on_construct.value, 4); + ASSERT_EQ(on_destroy.value, 4); + ASSERT_TRUE(pool.empty()); + + pool.insert(std::begin(entities), std::end(entities), 3); + + ASSERT_EQ(on_construct.value, 6); + ASSERT_EQ(on_destroy.value, 4); + ASSERT_FALSE(pool.empty()); + + ASSERT_EQ(pool.get(entities[0u]), 3); + ASSERT_EQ(pool.get(entities[1u]), 3); + + pool.erase(std::begin(entities), std::end(entities)); + + ASSERT_EQ(on_construct.value, 6); + ASSERT_EQ(on_destroy.value, 6); + ASSERT_TRUE(pool.empty()); +} + +TEST(SighMixin, StableType) { + entt::entity entities[2u]{entt::entity{3}, entt::entity{42}}; + entt::sigh_mixin> pool; + entt::sparse_set &base = pool; + entt::registry registry; + + counter on_construct{}; + counter on_destroy{}; + + pool.bind(entt::forward_as_any(registry)); + pool.on_construct().connect<&listener>(on_construct); + pool.on_destroy().connect<&listener>(on_destroy); + + ASSERT_NE(base.emplace(entities[0u]), base.end()); + + pool.emplace(entities[1u]); + + ASSERT_EQ(on_construct.value, 2); + ASSERT_EQ(on_destroy.value, 0); + ASSERT_FALSE(pool.empty()); + + ASSERT_EQ(pool.get(entities[0u]).value, 0); + ASSERT_EQ(pool.get(entities[1u]).value, 0); + + base.erase(entities[0u]); + pool.erase(entities[1u]); + + ASSERT_EQ(on_construct.value, 2); + ASSERT_EQ(on_destroy.value, 2); + ASSERT_FALSE(pool.empty()); + + ASSERT_NE(base.insert(std::begin(entities), std::end(entities)), base.end()); + + ASSERT_EQ(pool.get(entities[0u]).value, 0); + ASSERT_EQ(pool.get(entities[1u]).value, 0); + ASSERT_FALSE(pool.empty()); + + base.erase(entities[1u]); + + ASSERT_EQ(on_construct.value, 4); + ASSERT_EQ(on_destroy.value, 3); + ASSERT_FALSE(pool.empty()); + + base.erase(entities[0u]); + + ASSERT_EQ(on_construct.value, 4); + ASSERT_EQ(on_destroy.value, 4); + ASSERT_FALSE(pool.empty()); + + pool.insert(std::begin(entities), std::end(entities), stable_type{3}); + + ASSERT_EQ(on_construct.value, 6); + ASSERT_EQ(on_destroy.value, 4); + ASSERT_FALSE(pool.empty()); + + ASSERT_EQ(pool.get(entities[0u]).value, 3); + ASSERT_EQ(pool.get(entities[1u]).value, 3); + + pool.erase(std::begin(entities), std::end(entities)); + + ASSERT_EQ(on_construct.value, 6); + ASSERT_EQ(on_destroy.value, 6); + ASSERT_FALSE(pool.empty()); +} + +TEST(SighMixin, EmptyType) { + entt::entity entities[2u]{entt::entity{3}, entt::entity{42}}; + entt::sigh_mixin> pool; + entt::sparse_set &base = pool; + entt::registry registry; + + counter on_construct{}; + counter on_destroy{}; + + pool.bind(entt::forward_as_any(registry)); + pool.on_construct().connect<&listener>(on_construct); + pool.on_destroy().connect<&listener>(on_destroy); + + ASSERT_NE(base.emplace(entities[0u]), base.end()); + + pool.emplace(entities[1u]); + + ASSERT_EQ(on_construct.value, 2); + ASSERT_EQ(on_destroy.value, 0); + ASSERT_FALSE(pool.empty()); + + ASSERT_TRUE(pool.contains(entities[0u])); + ASSERT_TRUE(pool.contains(entities[1u])); + + base.erase(entities[0u]); + pool.erase(entities[1u]); + + ASSERT_EQ(on_construct.value, 2); + ASSERT_EQ(on_destroy.value, 2); + ASSERT_TRUE(pool.empty()); + + ASSERT_NE(base.insert(std::begin(entities), std::end(entities)), base.end()); + + ASSERT_TRUE(pool.contains(entities[0u])); + ASSERT_TRUE(pool.contains(entities[1u])); + ASSERT_FALSE(pool.empty()); + + base.erase(entities[1u]); + + ASSERT_EQ(on_construct.value, 4); + ASSERT_EQ(on_destroy.value, 3); + ASSERT_FALSE(pool.empty()); + + base.erase(entities[0u]); + + ASSERT_EQ(on_construct.value, 4); + ASSERT_EQ(on_destroy.value, 4); + ASSERT_TRUE(pool.empty()); + + pool.insert(std::begin(entities), std::end(entities)); + + ASSERT_EQ(on_construct.value, 6); + ASSERT_EQ(on_destroy.value, 4); + ASSERT_FALSE(pool.empty()); + + ASSERT_TRUE(pool.contains(entities[0u])); + ASSERT_TRUE(pool.contains(entities[1u])); + + pool.erase(std::begin(entities), std::end(entities)); + + ASSERT_EQ(on_construct.value, 6); + ASSERT_EQ(on_destroy.value, 6); + ASSERT_TRUE(pool.empty()); +} + +TEST(SighMixin, NonDefaultConstructibleType) { + entt::entity entities[2u]{entt::entity{3}, entt::entity{42}}; + entt::sigh_mixin> pool; + entt::sparse_set &base = pool; + entt::registry registry; + + counter on_construct{}; + counter on_destroy{}; + + pool.bind(entt::forward_as_any(registry)); + pool.on_construct().connect<&listener>(on_construct); + pool.on_destroy().connect<&listener>(on_destroy); + + ASSERT_EQ(base.emplace(entities[0u]), base.end()); + + pool.emplace(entities[1u], 3); + + ASSERT_EQ(pool.size(), 1u); + ASSERT_EQ(on_construct.value, 2); + ASSERT_EQ(on_destroy.value, 0); + ASSERT_FALSE(pool.empty()); + + ASSERT_FALSE(pool.contains(entities[0u])); + ASSERT_EQ(pool.get(entities[1u]).value, 3); + + base.erase(entities[1u]); + + ASSERT_EQ(pool.size(), 0u); + ASSERT_EQ(on_construct.value, 2); + ASSERT_EQ(on_destroy.value, 1); + ASSERT_TRUE(pool.empty()); + + ASSERT_EQ(base.insert(std::begin(entities), std::end(entities)), base.end()); + + ASSERT_FALSE(pool.contains(entities[0u])); + ASSERT_FALSE(pool.contains(entities[1u])); + ASSERT_TRUE(pool.empty()); + + pool.insert(std::begin(entities), std::end(entities), 3); + + ASSERT_EQ(pool.size(), 2u); + ASSERT_EQ(on_construct.value, 6); + ASSERT_EQ(on_destroy.value, 1); + ASSERT_FALSE(pool.empty()); + + ASSERT_EQ(pool.get(entities[0u]).value, 3); + ASSERT_EQ(pool.get(entities[1u]).value, 3); + + pool.erase(std::begin(entities), std::end(entities)); + + ASSERT_EQ(pool.size(), 0u); + ASSERT_EQ(on_construct.value, 6); + ASSERT_EQ(on_destroy.value, 3); + ASSERT_TRUE(pool.empty()); +} + +TEST(SighMixin, VoidType) { + entt::sigh_mixin> pool; + entt::registry registry; + + counter on_construct{}; + counter on_destroy{}; + + pool.bind(entt::forward_as_any(registry)); + pool.on_construct().connect<&listener>(on_construct); + pool.on_destroy().connect<&listener>(on_destroy); + + pool.emplace(entt::entity{99}); + + ASSERT_EQ(pool.type(), entt::type_id()); + ASSERT_TRUE(pool.contains(entt::entity{99})); + + entt::sigh_mixin> other{std::move(pool)}; + + ASSERT_FALSE(pool.contains(entt::entity{99})); + ASSERT_TRUE(other.contains(entt::entity{99})); + + pool = std::move(other); + + ASSERT_TRUE(pool.contains(entt::entity{99})); + ASSERT_FALSE(other.contains(entt::entity{99})); + + pool.clear(); + + ASSERT_EQ(on_construct.value, 1); + ASSERT_EQ(on_destroy.value, 1); +} + +TEST(SighMixin, Move) { + entt::sigh_mixin> pool; + entt::registry registry; + + counter on_construct{}; + counter on_destroy{}; + + pool.bind(entt::forward_as_any(registry)); + pool.on_construct().connect<&listener>(on_construct); + pool.on_destroy().connect<&listener>(on_destroy); + + pool.emplace(entt::entity{3}, 3); + + ASSERT_TRUE(std::is_move_constructible_v); + ASSERT_TRUE(std::is_move_assignable_v); + ASSERT_EQ(pool.type(), entt::type_id()); + + entt::sigh_mixin> other{std::move(pool)}; + + ASSERT_TRUE(pool.empty()); + ASSERT_FALSE(other.empty()); + ASSERT_EQ(other.type(), entt::type_id()); + ASSERT_EQ(pool.at(0u), static_cast(entt::null)); + ASSERT_EQ(other.at(0u), entt::entity{3}); + ASSERT_EQ(other.get(entt::entity{3}), 3); + + pool = std::move(other); + + ASSERT_FALSE(pool.empty()); + ASSERT_TRUE(other.empty()); + ASSERT_EQ(pool.at(0u), entt::entity{3}); + ASSERT_EQ(pool.get(entt::entity{3}), 3); + ASSERT_EQ(other.at(0u), static_cast(entt::null)); + + other = entt::sigh_mixin>{}; + other.bind(entt::forward_as_any(registry)); + + other.emplace(entt::entity{42}, 42); + other = std::move(pool); + + ASSERT_TRUE(pool.empty()); + ASSERT_FALSE(other.empty()); + ASSERT_EQ(pool.at(0u), static_cast(entt::null)); + ASSERT_EQ(other.at(0u), entt::entity{3}); + ASSERT_EQ(other.get(entt::entity{3}), 3); + + other.clear(); + + ASSERT_EQ(on_construct.value, 1); + ASSERT_EQ(on_destroy.value, 1); +} + +TEST(SighMixin, Swap) { + entt::sigh_mixin> pool; + entt::sigh_mixin> other; + entt::registry registry; + + counter on_construct{}; + counter on_destroy{}; + + pool.bind(entt::forward_as_any(registry)); + pool.on_construct().connect<&listener>(on_construct); + pool.on_destroy().connect<&listener>(on_destroy); + + other.bind(entt::forward_as_any(registry)); + other.on_construct().connect<&listener>(on_construct); + other.on_destroy().connect<&listener>(on_destroy); + + pool.emplace(entt::entity{42}, 41); + + other.emplace(entt::entity{9}, 8); + other.emplace(entt::entity{3}, 2); + other.erase(entt::entity{9}); + + ASSERT_EQ(pool.size(), 1u); + ASSERT_EQ(other.size(), 1u); + + pool.swap(other); + + ASSERT_EQ(pool.type(), entt::type_id()); + ASSERT_EQ(other.type(), entt::type_id()); + + ASSERT_EQ(pool.size(), 1u); + ASSERT_EQ(other.size(), 1u); + + ASSERT_EQ(pool.at(0u), entt::entity{3}); + ASSERT_EQ(pool.get(entt::entity{3}), 2); + + ASSERT_EQ(other.at(0u), entt::entity{42}); + ASSERT_EQ(other.get(entt::entity{42}), 41); + + pool.clear(); + other.clear(); + + ASSERT_EQ(on_construct.value, 3); + ASSERT_EQ(on_destroy.value, 3); +} + +TEST(SighMixin, CustomAllocator) { + auto test = [](auto pool, auto alloc) { + using registry_type = typename decltype(pool)::registry_type; + registry_type registry; + + counter on_construct{}; + counter on_destroy{}; + + pool.bind(entt::forward_as_any(registry)); + pool.on_construct().template connect<&listener>(on_construct); + pool.on_destroy().template connect<&listener>(on_destroy); + + pool.reserve(1u); + + ASSERT_NE(pool.capacity(), 0u); + + pool.emplace(entt::entity{0}); + pool.emplace(entt::entity{1}); + + decltype(pool) other{std::move(pool), alloc}; + + ASSERT_TRUE(pool.empty()); + ASSERT_FALSE(other.empty()); + ASSERT_EQ(pool.capacity(), 0u); + ASSERT_NE(other.capacity(), 0u); + ASSERT_EQ(other.size(), 2u); + + pool = std::move(other); + + ASSERT_FALSE(pool.empty()); + ASSERT_TRUE(other.empty()); + ASSERT_EQ(other.capacity(), 0u); + ASSERT_NE(pool.capacity(), 0u); + ASSERT_EQ(pool.size(), 2u); + + pool.swap(other); + pool = std::move(other); + + ASSERT_FALSE(pool.empty()); + ASSERT_TRUE(other.empty()); + ASSERT_EQ(other.capacity(), 0u); + ASSERT_NE(pool.capacity(), 0u); + ASSERT_EQ(pool.size(), 2u); + + pool.clear(); + + ASSERT_NE(pool.capacity(), 0u); + ASSERT_EQ(pool.size(), 0u); + + ASSERT_EQ(on_construct.value, 2); + ASSERT_EQ(on_destroy.value, 2); + }; + + test::throwing_allocator allocator{}; + + test(entt::sigh_mixin>>{allocator}, allocator); + test(entt::sigh_mixin>>{allocator}, allocator); + test(entt::sigh_mixin>>{allocator}, allocator); +} + +TEST(SighMixin, ThrowingAllocator) { + auto test = [](auto pool) { + using pool_allocator_type = typename decltype(pool)::allocator_type; + using value_type = typename decltype(pool)::value_type; + using registry_type = typename decltype(pool)::registry_type; + + typename std::decay_t::base_type &base = pool; + constexpr auto packed_page_size = entt::component_traits::page_size; + constexpr auto sparse_page_size = entt::entt_traits::page_size; + registry_type registry; + + counter on_construct{}; + counter on_destroy{}; + + pool.bind(entt::forward_as_any(registry)); + pool.on_construct().template connect<&listener>(on_construct); + pool.on_destroy().template connect<&listener>(on_destroy); + + pool_allocator_type::trigger_on_allocate = true; + + ASSERT_THROW(pool.reserve(1u), typename pool_allocator_type::exception_type); + ASSERT_EQ(pool.capacity(), 0u); + + pool_allocator_type::trigger_after_allocate = true; + + ASSERT_THROW(pool.reserve(2 * packed_page_size), typename pool_allocator_type::exception_type); + ASSERT_EQ(pool.capacity(), packed_page_size); + + pool.shrink_to_fit(); + + ASSERT_EQ(pool.capacity(), 0u); + + test::throwing_allocator::trigger_on_allocate = true; + + ASSERT_THROW(pool.emplace(entt::entity{0}, 0), test::throwing_allocator::exception_type); + ASSERT_FALSE(pool.contains(entt::entity{0})); + ASSERT_TRUE(pool.empty()); + + test::throwing_allocator::trigger_on_allocate = true; + + ASSERT_THROW(base.emplace(entt::entity{0}), test::throwing_allocator::exception_type); + ASSERT_FALSE(base.contains(entt::entity{0})); + ASSERT_TRUE(base.empty()); + + pool_allocator_type::trigger_on_allocate = true; + + ASSERT_THROW(pool.emplace(entt::entity{0}, 0), typename pool_allocator_type::exception_type); + ASSERT_FALSE(pool.contains(entt::entity{0})); + ASSERT_NO_FATAL_FAILURE(pool.compact()); + ASSERT_TRUE(pool.empty()); + + pool.emplace(entt::entity{0}, 0); + const entt::entity entities[2u]{entt::entity{1}, entt::entity{sparse_page_size}}; + test::throwing_allocator::trigger_after_allocate = true; + + ASSERT_THROW(pool.insert(std::begin(entities), std::end(entities), value_type{0}), test::throwing_allocator::exception_type); + ASSERT_TRUE(pool.contains(entt::entity{1})); + ASSERT_FALSE(pool.contains(entt::entity{sparse_page_size})); + + pool.erase(entt::entity{1}); + const value_type components[2u]{value_type{1}, value_type{sparse_page_size}}; + test::throwing_allocator::trigger_on_allocate = true; + pool.compact(); + + ASSERT_THROW(pool.insert(std::begin(entities), std::end(entities), std::begin(components)), test::throwing_allocator::exception_type); + ASSERT_TRUE(pool.contains(entt::entity{1})); + ASSERT_FALSE(pool.contains(entt::entity{sparse_page_size})); + + ASSERT_EQ(on_construct.value, 1); + ASSERT_EQ(on_destroy.value, 1); + }; + + test(entt::sigh_mixin>>{}); + test(entt::sigh_mixin>>{}); +} + +TEST(SighMixin, ThrowingComponent) { + entt::sigh_mixin> pool; + using registry_type = typename decltype(pool)::registry_type; + registry_type registry; + + counter on_construct{}; + counter on_destroy{}; + + pool.bind(entt::forward_as_any(registry)); + pool.on_construct().connect<&listener>(on_construct); + pool.on_destroy().connect<&listener>(on_destroy); + + test::throwing_type::trigger_on_value = 42; + + // strong exception safety + ASSERT_THROW(pool.emplace(entt::entity{0}, test::throwing_type{42}), typename test::throwing_type::exception_type); + ASSERT_TRUE(pool.empty()); + + const entt::entity entities[2u]{entt::entity{42}, entt::entity{1}}; + const test::throwing_type components[2u]{42, 1}; + + // basic exception safety + ASSERT_THROW(pool.insert(std::begin(entities), std::end(entities), test::throwing_type{42}), typename test::throwing_type::exception_type); + ASSERT_EQ(pool.size(), 0u); + ASSERT_FALSE(pool.contains(entt::entity{1})); + + // basic exception safety + ASSERT_THROW(pool.insert(std::begin(entities), std::end(entities), std::begin(components)), typename test::throwing_type::exception_type); + ASSERT_EQ(pool.size(), 0u); + ASSERT_FALSE(pool.contains(entt::entity{1})); + + // basic exception safety + ASSERT_THROW(pool.insert(std::rbegin(entities), std::rend(entities), std::rbegin(components)), typename test::throwing_type::exception_type); + ASSERT_EQ(pool.size(), 1u); + ASSERT_TRUE(pool.contains(entt::entity{1})); + ASSERT_EQ(pool.get(entt::entity{1}), 1); + + pool.clear(); + pool.emplace(entt::entity{1}, 1); + pool.emplace(entt::entity{42}, 42); + + // basic exception safety + ASSERT_THROW(pool.erase(entt::entity{1}), typename test::throwing_type::exception_type); + ASSERT_EQ(pool.size(), 2u); + ASSERT_TRUE(pool.contains(entt::entity{42})); + ASSERT_TRUE(pool.contains(entt::entity{1})); + ASSERT_EQ(pool.at(0u), entt::entity{1}); + ASSERT_EQ(pool.at(1u), entt::entity{42}); + ASSERT_EQ(pool.get(entt::entity{42}), 42); + // the element may have been moved but it's still there + ASSERT_EQ(pool.get(entt::entity{1}), test::throwing_type::moved_from_value); + + test::throwing_type::trigger_on_value = 99; + pool.erase(entt::entity{1}); + + ASSERT_EQ(pool.size(), 1u); + ASSERT_TRUE(pool.contains(entt::entity{42})); + ASSERT_FALSE(pool.contains(entt::entity{1})); + ASSERT_EQ(pool.at(0u), entt::entity{42}); + ASSERT_EQ(pool.get(entt::entity{42}), 42); + + ASSERT_EQ(on_construct.value, 2); + ASSERT_EQ(on_destroy.value, 3); +}