// Catch static asserts #define flecs_static_assert(cond, str)\ ecs_assert(cond, ECS_INVALID_OPERATION, str) #include int Pod::ctor_invoked = 0; int Pod::dtor_invoked = 0; int Pod::copy_invoked = 0; int Pod::move_invoked = 0; int Pod::copy_ctor_invoked = 0; int Pod::move_ctor_invoked = 0; int CountNoDefaultCtor::ctor_invoked = 0; int CountNoDefaultCtor::dtor_invoked = 0; int CountNoDefaultCtor::copy_invoked = 0; int CountNoDefaultCtor::move_invoked = 0; int CountNoDefaultCtor::copy_ctor_invoked = 0; int CountNoDefaultCtor::move_ctor_invoked = 0; void ComponentLifecycle_ctor_on_add(void) { flecs::world world; world.component(); auto e = world.entity().add(); test_assert(e.id() != 0); test_assert(e.has()); const Pod *pod = e.get(); test_assert(pod != NULL); test_int(Pod::ctor_invoked, 1); test_int(Pod::dtor_invoked, 0); test_int(Pod::copy_invoked, 0); test_int(Pod::move_invoked, 0); test_int(pod->value, 10); } void ComponentLifecycle_dtor_on_remove(void) { flecs::world world; world.component(); auto e = world.entity().add(); test_assert(e.id() != 0); test_assert(e.has()); test_int(Pod::ctor_invoked, 1); e.remove(); test_assert(!e.has()); test_int(Pod::ctor_invoked, 1); test_int(Pod::dtor_invoked, 1); test_int(Pod::copy_invoked, 0); test_int(Pod::move_invoked, 0); } void ComponentLifecycle_move_on_add(void) { flecs::world world; world.component(); auto e = world.entity().add(); test_assert(e.id() != 0); test_assert(e.has()); const Pod *pod = e.get(); test_assert(pod != NULL); test_int(Pod::ctor_invoked, 1); test_int(Pod::dtor_invoked, 0); test_int(Pod::copy_invoked, 0); test_int(Pod::move_invoked, 0); test_int(Pod::copy_ctor_invoked, 0); test_int(Pod::move_ctor_invoked, 0); e.add(); test_int(Pod::ctor_invoked, 1); test_int(Pod::dtor_invoked, 1); test_int(Pod::copy_invoked, 0); test_int(Pod::move_invoked, 0); test_int(Pod::copy_ctor_invoked, 0); test_int(Pod::move_ctor_invoked, 1); test_int(pod->value, 10); } void ComponentLifecycle_move_on_remove(void) { flecs::world world; world.component(); auto e = world.entity().add().add(); test_assert(e.id() != 0); test_assert(e.has()); const Pod *pod = e.get(); test_assert(pod != NULL); test_int(Pod::ctor_invoked, 1); test_int(Pod::dtor_invoked, 0); test_int(Pod::copy_invoked, 0); test_int(Pod::move_invoked, 0); test_int(Pod::copy_ctor_invoked, 0); test_int(Pod::move_ctor_invoked, 0); e.remove(); test_int(Pod::ctor_invoked, 1); test_int(Pod::dtor_invoked, 1); test_int(Pod::copy_invoked, 0); test_int(Pod::move_invoked, 0); test_int(Pod::copy_ctor_invoked, 0); test_int(Pod::move_ctor_invoked, 1); test_int(pod->value, 10); } void ComponentLifecycle_copy_on_set(void) { flecs::world world; world.component(); auto e = world.entity(); test_assert(e.id() != 0); e.set({20}); test_assert(e.has()); test_int(Pod::ctor_invoked, 2); test_int(Pod::dtor_invoked, 1); test_int(Pod::copy_invoked, 0); test_int(Pod::move_invoked, 1); } void ComponentLifecycle_copy_on_override(void) { flecs::world world; world.component(); auto base = world.entity(); test_assert(base.id() != 0); base.set({10}); test_int(Pod::ctor_invoked, 2); test_int(Pod::dtor_invoked, 1); test_int(Pod::copy_invoked, 0); Pod::ctor_invoked = 0; Pod::dtor_invoked = 0; Pod::copy_invoked = 0; Pod::move_invoked = 0; auto e = world.entity(); test_assert(e.id() != 0); e.add(flecs::IsA, base); test_int(Pod::ctor_invoked, 0); e.add(); test_int(Pod::ctor_invoked, 1); test_int(Pod::dtor_invoked, 0); test_int(Pod::copy_invoked, 1); test_int(Pod::move_invoked, 0); const Pod *pod = e.get(); test_assert(pod != NULL); test_int(pod->value, 10); } void ComponentLifecycle_struct_w_string_add(void) { flecs::world world; auto e = world.entity().add(); test_assert(e.id() != 0); test_assert(e.has()); const Struct_w_string *str = e.get(); test_assert(str != NULL); test_assert(str->value == ""); } void ComponentLifecycle_struct_w_string_remove(void) { flecs::world world; auto e = world.entity().add(); test_assert(e.id() != 0); test_assert(e.has()); e.remove(); test_assert(!e.has()); } void ComponentLifecycle_struct_w_string_set(void) { flecs::world world; auto e = world.entity() .set({"Hello World"}); test_assert(e.id() != 0); test_assert(e.has()); const Struct_w_string *str = e.get(); test_assert(str != NULL); test_assert(str->value == "Hello World"); } void ComponentLifecycle_struct_w_string_override(void) { flecs::world world; auto base = world.entity(); test_assert(base.id() != 0); base.set({"Hello World"}); auto e = world.entity(); test_assert(e.id() != 0); e.add(flecs::IsA, base); e.add(); const Struct_w_string *str = e.get(); test_assert(str != NULL); test_assert(str->value == "Hello World"); } void ComponentLifecycle_struct_w_string_add_2_remove(void) { flecs::world world; auto e1 = world.entity().add(); auto e2 = world.entity().add(); const Struct_w_string *str1 = e1.get(); test_assert(str1 != NULL); test_assert(str1->value == ""); const Struct_w_string *str2 = e2.get(); test_assert(str2 != NULL); test_assert(str2->value == ""); e1.remove(); str1 = e1.get(); test_assert(str1 == NULL); str2 = e2.get(); test_assert(str2 != NULL); test_assert(str2->value == ""); e2.remove(); str2 = e2.get(); test_assert(str2 == NULL); } void ComponentLifecycle_struct_w_string_set_2_remove(void) { flecs::world world; auto e1 = world.entity().set({"hello"}); auto e2 = world.entity().set({"world"}); const Struct_w_string *str1 = e1.get(); test_assert(str1 != NULL); test_assert(str1->value == "hello"); const Struct_w_string *str2 = e2.get(); test_assert(str2 != NULL); test_assert(str2->value == "world"); e1.remove(); str1 = e1.get(); test_assert(str1 == NULL); str2 = e2.get(); test_assert(str2 != NULL); test_assert(str2->value == "world"); e2.remove(); str2 = e2.get(); test_assert(str2 == NULL); } void ComponentLifecycle_struct_w_string_add_2_remove_w_tag(void) { flecs::world world; auto e1 = world.entity().add().add(); auto e2 = world.entity().add().add(); const Struct_w_string *str1 = e1.get(); test_assert(str1 != NULL); test_assert(str1->value == ""); const Struct_w_string *str2 = e2.get(); test_assert(str2 != NULL); test_assert(str2->value == ""); e1.remove(); str1 = e1.get(); test_assert(str1 == NULL); str2 = e2.get(); test_assert(str2 != NULL); test_assert(str2->value == ""); e2.remove(); str2 = e2.get(); test_assert(str2 == NULL); } void ComponentLifecycle_struct_w_string_set_2_remove_w_tag(void) { flecs::world world; auto e1 = world.entity().add().set({"hello"}); auto e2 = world.entity().add().set({"world"}); const Struct_w_string *str1 = e1.get(); test_assert(str1 != NULL); test_assert(str1->value == "hello"); const Struct_w_string *str2 = e2.get(); test_assert(str2 != NULL); test_assert(str2->value == "world"); e1.remove(); str1 = e1.get(); test_assert(str1 == NULL); str2 = e2.get(); test_assert(str2 != NULL); test_assert(str2->value == "world"); e2.remove(); str2 = e2.get(); test_assert(str2 == NULL); } void ComponentLifecycle_struct_w_vector_add(void) { flecs::world world; auto e = world.entity().add(); test_assert(e.has()); const Struct_w_vector *ptr = e.get(); test_assert(ptr != NULL); test_int(ptr->value.size(), 0); } void ComponentLifecycle_struct_w_vector_remove(void) { flecs::world world; auto e = world.entity().add(); test_assert(e.has()); e.remove(); test_assert(!e.has()); } void ComponentLifecycle_struct_w_vector_set(void) { flecs::world world; auto e = world.entity().set({std::vector{1, 2}}); test_assert(e.has()); const Struct_w_vector *ptr = e.get(); test_assert(ptr != NULL); test_int(ptr->value.size(), 2); test_int(ptr->value.at(0), 1); test_int(ptr->value.at(1), 2); } void ComponentLifecycle_struct_w_vector_override(void) { flecs::world world; auto base = world.entity().set({std::vector{1, 2}}); test_assert(base.has()); auto e = world.entity().is_a(base).add(); test_assert(e.has()); const Struct_w_vector *ptr = base.get(); test_assert(ptr != NULL); test_int(ptr->value.size(), 2); test_int(ptr->value.at(0), 1); test_int(ptr->value.at(1), 2); ptr = e.get(); test_assert(ptr != NULL); test_int(ptr->value.size(), 2); test_int(ptr->value.at(0), 1); test_int(ptr->value.at(1), 2); } void ComponentLifecycle_struct_w_vector_add_2_remove(void) { flecs::world world; auto e1 = world.entity().add(); auto e2 = world.entity().add(); const Struct_w_vector *ptr1 = e1.get(); test_assert(ptr1 != NULL); test_int(ptr1->value.size(), 0); const Struct_w_vector *ptr2 = e2.get(); test_assert(ptr2 != NULL); test_int(ptr2->value.size(), 0); e1.remove(); ptr1 = e1.get(); test_assert(ptr1 == NULL); ptr2 = e2.get(); test_assert(ptr2 != NULL); test_int(ptr2->value.size(), 0); e2.remove(); ptr2 = e2.get(); test_assert(ptr2 == NULL); } void ComponentLifecycle_struct_w_vector_set_2_remove(void) { flecs::world world; auto e1 = world.entity().set({std::vector{1, 2}}); auto e2 = world.entity().set({std::vector{3, 4}}); const Struct_w_vector *ptr1 = e1.get(); test_assert(ptr1 != NULL); test_int(ptr1->value.size(), 2); test_int(ptr1->value.at(0), 1); test_int(ptr1->value.at(1), 2); const Struct_w_vector *ptr2 = e2.get(); test_assert(ptr2 != NULL); test_int(ptr2->value.size(), 2); test_int(ptr2->value.at(0), 3); test_int(ptr2->value.at(1), 4); e1.remove(); ptr1 = e1.get(); test_assert(ptr1 == NULL); ptr2 = e2.get(); test_assert(ptr2 != NULL); test_int(ptr2->value.size(), 2); test_int(ptr2->value.at(0), 3); test_int(ptr2->value.at(1), 4); e2.remove(); ptr2 = e2.get(); test_assert(ptr2 == NULL); } void ComponentLifecycle_struct_w_vector_add_2_remove_w_tag(void) { flecs::world world; auto e1 = world.entity().add().add(); auto e2 = world.entity().add().add(); const Struct_w_vector *ptr1 = e1.get(); test_assert(ptr1 != NULL); test_int(ptr1->value.size(), 0); const Struct_w_vector *ptr2 = e2.get(); test_assert(ptr2 != NULL); test_int(ptr2->value.size(), 0); e1.remove(); ptr1 = e1.get(); test_assert(ptr1 == NULL); ptr2 = e2.get(); test_assert(ptr2 != NULL); test_int(ptr2->value.size(), 0); e2.remove(); ptr2 = e2.get(); test_assert(ptr2 == NULL); } void ComponentLifecycle_struct_w_vector_set_2_remove_w_tag(void) { flecs::world world; auto e1 = world.entity().add().set({std::vector{1, 2}}); auto e2 = world.entity().add().set({std::vector{3, 4}}); const Struct_w_vector *ptr1 = e1.get(); test_assert(ptr1 != NULL); test_int(ptr1->value.size(), 2); test_int(ptr1->value.at(0), 1); test_int(ptr1->value.at(1), 2); const Struct_w_vector *ptr2 = e2.get(); test_assert(ptr2 != NULL); test_int(ptr2->value.size(), 2); test_int(ptr2->value.at(0), 3); test_int(ptr2->value.at(1), 4); e1.remove(); ptr1 = e1.get(); test_assert(ptr1 == NULL); ptr2 = e2.get(); test_assert(ptr2 != NULL); test_int(ptr2->value.size(), 2); test_int(ptr2->value.at(0), 3); test_int(ptr2->value.at(1), 4); e2.remove(); ptr2 = e2.get(); test_assert(ptr2 == NULL); } void ComponentLifecycle_get_mut_new(void) { flecs::world world; world.component(); auto e = world.entity(); test_assert(e.id() != 0); Pod* value = e.get_mut(); test_assert(value != NULL); Pod::ctor_invoked = 1; Pod::dtor_invoked = 0; Pod::copy_invoked = 0; Pod::move_invoked = 0; e.modified(); Pod::ctor_invoked = 1; Pod::dtor_invoked = 0; Pod::copy_invoked = 0; Pod::move_invoked = 0; } void ComponentLifecycle_get_mut_existing(void) { flecs::world world; world.component(); auto e = world.entity(); test_assert(e.id() != 0); Pod* value = e.get_mut(); test_assert(value != NULL); Pod::ctor_invoked = 1; Pod::dtor_invoked = 0; Pod::copy_invoked = 0; Pod::move_invoked = 0; value = e.get_mut(); test_assert(value != NULL); /* Repeated calls to get_mut should not invoke constructor */ Pod::ctor_invoked = 1; Pod::dtor_invoked = 0; Pod::copy_invoked = 0; Pod::move_invoked = 0; } void ComponentLifecycle_implicit_component(void) { flecs::world world; auto e = world.entity().add(); test_assert(e.id() != 0); test_assert(e.has()); test_int(Pod::ctor_invoked, 1); const Pod *pod = e.get(); test_assert(pod != NULL); test_int(pod->value, 10); test_int(Pod::ctor_invoked, 1); test_int(Pod::dtor_invoked, 0); test_int(Pod::copy_invoked, 0); test_int(Pod::move_invoked, 0); world.entity().add(); test_int(Pod::ctor_invoked, 2); test_int(Pod::move_ctor_invoked, 0); test_int(Pod::move_invoked, 0); world.entity().add(); test_int(Pod::ctor_invoked, 3); test_int(Pod::move_ctor_invoked, 2); test_int(Pod::move_invoked, 0); } void ComponentLifecycle_implicit_after_query(void) { flecs::world world; world.query(); auto e = world.entity().add(); test_assert(e.id() != 0); test_assert(e.has()); test_int(Pod::ctor_invoked, 1); const Pod *pod = e.get(); test_assert(pod != NULL); test_int(pod->value, 10); test_int(Pod::ctor_invoked, 1); test_int(Pod::dtor_invoked, 0); test_int(Pod::copy_invoked, 0); test_int(Pod::move_invoked, 0); world.entity().add(); test_int(Pod::ctor_invoked, 2); test_int(Pod::move_ctor_invoked, 0); test_int(Pod::move_invoked, 0); world.entity().add(); test_int(Pod::ctor_invoked, 3); test_int(Pod::move_ctor_invoked, 2); test_int(Pod::move_invoked, 0); } template static void try_add(flecs::world& ecs) { flecs::entity e = ecs.entity().add(); test_assert(e.has()); const T *ptr = e.get(); test_int(ptr->x_, 99); e.remove(); test_assert(!e.has()); } template static void try_add_relation(flecs::world& ecs) { flecs::entity obj = ecs.entity(); flecs::entity e = ecs.entity().add(obj); test_assert(e.has()); const T *ptr = e.get(); test_int(ptr->x_, 89); e.remove(); test_assert(!e.has()); } template static void try_add_second(flecs::world& ecs) { flecs::entity rel = ecs.entity(); flecs::entity e = ecs.entity().add_second(rel); test_assert(e.has()); const T *ptr = e.get(); test_int(ptr->x_, 89); e.remove(); test_assert(!e.has()); } template static void try_set(flecs::world& ecs) { flecs::entity e = ecs.entity().set({10}); const T *ptr = e.get(); test_int(ptr->x_, 10); } template static void try_emplace(flecs::world& ecs) { flecs::entity e = ecs.entity().emplace(10); const T *ptr = e.get(); test_int(ptr->x_, 10); } template static void try_set_default(flecs::world& ecs) { flecs::entity e = ecs.entity().set(T()); const T *ptr = e.get(); test_int(ptr->x_, 99); e.remove(); } void ComponentLifecycle_deleted_copy(void) { flecs::world ecs; ecs.component(); try_add(ecs); try_set(ecs); } void ComponentLifecycle_no_default_ctor_emplace(void) { flecs::world ecs; ecs.component(); try_emplace(ecs); } void ComponentLifecycle_default_init(void) { flecs::world ecs; ecs.component(); try_add(ecs); try_set(ecs); } void ComponentLifecycle_no_default_ctor_add(void) { install_test_abort(); flecs::world ecs; ecs.component(); test_expect_abort(); try_add(ecs); } void ComponentLifecycle_no_default_ctor_add_relation(void) { install_test_abort(); flecs::world ecs; ecs.component(); test_expect_abort(); try_add_relation(ecs); } void ComponentLifecycle_no_default_ctor_add_second(void) { install_test_abort(); flecs::world ecs; ecs.component(); test_expect_abort(); try_add_second(ecs); } void ComponentLifecycle_no_default_ctor_set(void) { install_test_abort(); flecs::world ecs; ecs.component(); test_expect_abort(); try_set(ecs); } void ComponentLifecycle_no_copy_ctor(void) { flecs::world ecs; ecs.component(); try_add(ecs); try_set(ecs); } void ComponentLifecycle_no_move_ctor(void) { install_test_abort(); flecs::world ecs; test_expect_abort(); ecs.component(); } void ComponentLifecycle_no_copy_assign(void) { flecs::world ecs; ecs.component(); try_add(ecs); try_set(ecs); } void ComponentLifecycle_no_move_assign(void) { install_test_abort(); flecs::world ecs; test_expect_abort(); ecs.component(); } void ComponentLifecycle_no_copy(void) { flecs::world ecs; ecs.component(); try_add(ecs); try_set(ecs); } void ComponentLifecycle_no_move(void) { install_test_abort(); flecs::world ecs; test_expect_abort(); ecs.component(); } void ComponentLifecycle_no_dtor(void) { install_test_abort(); flecs::world ecs; test_expect_abort(); ecs.component(); } void ComponentLifecycle_default_ctor_w_value_ctor(void) { flecs::world ecs; ecs.component(); try_add(ecs); try_set(ecs); try_set_default(ecs); } void ComponentLifecycle_no_default_ctor_move_ctor_on_set(void) { flecs::world ecs; ecs.component(); // Emplace, construct auto e = ecs.entity().emplace(10); test_assert(e.has()); const CountNoDefaultCtor* ptr = e.get(); test_assert(ptr != NULL); test_int(ptr->value, 10); test_int(CountNoDefaultCtor::ctor_invoked, 1); test_int(CountNoDefaultCtor::dtor_invoked, 0); test_int(CountNoDefaultCtor::copy_invoked, 0); test_int(CountNoDefaultCtor::move_invoked, 0); test_int(CountNoDefaultCtor::copy_ctor_invoked, 0); test_int(CountNoDefaultCtor::move_ctor_invoked, 0); // Set, move assign e.set({10}); test_int(CountNoDefaultCtor::ctor_invoked, 2); test_int(CountNoDefaultCtor::dtor_invoked, 1); test_int(CountNoDefaultCtor::copy_invoked, 0); test_int(CountNoDefaultCtor::move_invoked, 1); test_int(CountNoDefaultCtor::copy_ctor_invoked, 0); test_int(CountNoDefaultCtor::move_ctor_invoked, 0); } void ComponentLifecycle_emplace_w_ctor(void) { flecs::world ecs; auto e = ecs.entity() .emplace(10); test_int(Pod::ctor_invoked, 1); test_int(Pod::dtor_invoked, 0); const Pod *ptr = e.get(); test_assert(ptr != NULL); test_int(ptr->value, 10); test_int(Pod::ctor_invoked, 1); test_int(Pod::dtor_invoked, 0); } void ComponentLifecycle_emplace_no_default_ctor(void) { flecs::world ecs; auto e = ecs.entity() .emplace(10); test_int(CountNoDefaultCtor::ctor_invoked, 1); test_int(CountNoDefaultCtor::dtor_invoked, 0); const CountNoDefaultCtor *ptr = e.get(); test_assert(ptr != NULL); test_int(ptr->value, 10); test_int(CountNoDefaultCtor::ctor_invoked, 1); test_int(CountNoDefaultCtor::dtor_invoked, 0); } void ComponentLifecycle_emplace_defer_use_move_ctor(void) { { flecs::world ecs; auto e = ecs.entity(); ecs.defer_begin(); e.emplace(10); test_assert(!e.has()); test_int(CountNoDefaultCtor::ctor_invoked, 1); test_int(CountNoDefaultCtor::dtor_invoked, 0); test_int(CountNoDefaultCtor::move_invoked, 0); test_int(CountNoDefaultCtor::move_ctor_invoked, 0); ecs.defer_end(); test_assert(e.has()); test_int(CountNoDefaultCtor::ctor_invoked, 1); test_int(CountNoDefaultCtor::dtor_invoked, 1); test_int(CountNoDefaultCtor::move_invoked, 0); test_int(CountNoDefaultCtor::move_ctor_invoked, 1); const CountNoDefaultCtor *ptr = e.get(); test_assert(ptr != NULL); test_int(ptr->value, 10); test_int(CountNoDefaultCtor::ctor_invoked, 1); test_int(CountNoDefaultCtor::dtor_invoked, 1); test_int(CountNoDefaultCtor::move_invoked, 0); test_int(CountNoDefaultCtor::move_ctor_invoked, 1); } test_int(CountNoDefaultCtor::ctor_invoked, 1); test_int(CountNoDefaultCtor::dtor_invoked, 2); test_int(CountNoDefaultCtor::move_invoked, 0); test_int(CountNoDefaultCtor::move_ctor_invoked, 1); } void ComponentLifecycle_emplace_existing(void) { install_test_abort(); flecs::world ecs; auto e = ecs.entity() .emplace(10); const Pod *ptr = e.get(); test_assert(ptr != NULL); test_int(ptr->value, 10); test_int(Pod::ctor_invoked, 1); test_int(Pod::dtor_invoked, 0); test_expect_abort(); e.emplace(20); } void ComponentLifecycle_emplace_singleton(void) { flecs::world ecs; ecs.emplace(10); test_int(Pod::ctor_invoked, 1); test_int(Pod::dtor_invoked, 0); const Pod *ptr = ecs.get(); test_assert(ptr != NULL); test_int(ptr->value, 10); test_int(Pod::ctor_invoked, 1); test_int(Pod::dtor_invoked, 0); } class CtorDtorNonTrivial { public: CtorDtorNonTrivial(int x) : x_(x) { ctor_invoked ++; } ~CtorDtorNonTrivial() { dtor_invoked ++; dtor_value = x_; } int x_; std::string str_; static int ctor_invoked; static int dtor_invoked; static int dtor_value; }; int CtorDtorNonTrivial::ctor_invoked; int CtorDtorNonTrivial::dtor_invoked; int CtorDtorNonTrivial::dtor_value; void ComponentLifecycle_dtor_w_non_trivial_implicit_move(void) { flecs::world ecs; test_bool(std::is_trivially_move_assignable::value, false); test_bool(std::is_move_assignable::value, true); auto e_1 = ecs.entity().emplace(10); auto e_2 = ecs.entity().emplace(20); const CtorDtorNonTrivial *ptr = e_1.get(); test_assert(ptr != nullptr); test_int(ptr->x_, 10); ptr = e_2.get(); test_assert(ptr != nullptr); test_int(ptr->x_, 20); test_int(CtorDtorNonTrivial::ctor_invoked, 2); // Moves e_2 to e_1 e_1.destruct(); test_int(CtorDtorNonTrivial::ctor_invoked, 2); test_int(CtorDtorNonTrivial::dtor_invoked, 1); // Counter intuitive but correct. The class is not trivially movable, so // the move assignment should take care of cleaning up e_1 (10). That still // leaves the original e_2 which was moved from, but not destructed. // // In a real application the class should probably implement its own move // assignment to ensure correct destructor behavior. test_int(CtorDtorNonTrivial::dtor_value, 20); } class CtorDtor_w_MoveAssign { public: CtorDtor_w_MoveAssign(int x) : x_(x) { ctor_invoked ++; } ~CtorDtor_w_MoveAssign() { dtor_invoked ++; dtor_value = x_; } CtorDtor_w_MoveAssign(const CtorDtor_w_MoveAssign& obj) = default; CtorDtor_w_MoveAssign(CtorDtor_w_MoveAssign&& obj) = default; CtorDtor_w_MoveAssign& operator=(const CtorDtor_w_MoveAssign& obj) = default; CtorDtor_w_MoveAssign& operator=(CtorDtor_w_MoveAssign&& obj) { move_value = this->x_; this->x_ = obj.x_; obj.x_ = 0; return *this; } int x_; std::string str_; static int ctor_invoked; static int dtor_invoked; static int dtor_value; static int move_value; }; int CtorDtor_w_MoveAssign::ctor_invoked; int CtorDtor_w_MoveAssign::dtor_invoked; int CtorDtor_w_MoveAssign::dtor_value; int CtorDtor_w_MoveAssign::move_value; void ComponentLifecycle_dtor_w_non_trivial_explicit_move(void) { flecs::world ecs; test_bool(std::is_trivially_move_assignable::value, false); test_bool(std::is_move_assignable::value, true); auto e_1 = ecs.entity().emplace(10); auto e_2 = ecs.entity().emplace(20); const CtorDtor_w_MoveAssign *ptr = e_1.get(); test_assert(ptr != nullptr); test_int(ptr->x_, 10); ptr = e_2.get(); test_assert(ptr != nullptr); test_int(ptr->x_, 20); test_int(CtorDtor_w_MoveAssign::ctor_invoked, 2); // Moves e_2 to e_1 e_1.destruct(); test_int(CtorDtor_w_MoveAssign::ctor_invoked, 2); test_int(CtorDtor_w_MoveAssign::dtor_invoked, 1); test_int(CtorDtor_w_MoveAssign::move_value, 10); test_int(CtorDtor_w_MoveAssign::dtor_value, 0); } void ComponentLifecycle_grow_no_default_ctor(void) { { flecs::world world; world.component(); auto e1 = world.entity().emplace(1); auto e2 = world.entity().emplace(2); test_int(CountNoDefaultCtor::ctor_invoked, 2); test_int(CountNoDefaultCtor::dtor_invoked, 0); test_int(CountNoDefaultCtor::copy_invoked, 0); test_int(CountNoDefaultCtor::move_invoked, 0); test_int(CountNoDefaultCtor::copy_ctor_invoked, 0); test_int(CountNoDefaultCtor::move_ctor_invoked, 0); auto e3 = world.entity().emplace(3); test_int(CountNoDefaultCtor::ctor_invoked, 3); test_int(CountNoDefaultCtor::dtor_invoked, 2); test_int(CountNoDefaultCtor::copy_invoked, 0); test_int(CountNoDefaultCtor::move_invoked, 0); test_int(CountNoDefaultCtor::copy_ctor_invoked, 0); test_int(CountNoDefaultCtor::move_ctor_invoked, 2); test_assert(e1.has()); test_assert(e2.has()); test_assert(e3.has()); test_int(e1.get()->value, 1); test_int(e2.get()->value, 2); test_int(e3.get()->value, 3); } test_int(CountNoDefaultCtor::ctor_invoked, 3); test_int(CountNoDefaultCtor::dtor_invoked, 5); test_int(CountNoDefaultCtor::copy_invoked, 0); test_int(CountNoDefaultCtor::move_invoked, 0); test_int(CountNoDefaultCtor::copy_ctor_invoked, 0); test_int(CountNoDefaultCtor::move_ctor_invoked, 2); } void ComponentLifecycle_grow_no_default_ctor_move(void) { { flecs::world world; world.component(); world.component(); auto e1 = world.entity().emplace(1); auto e2 = world.entity().emplace(2); test_int(CountNoDefaultCtor::ctor_invoked, 2); test_int(CountNoDefaultCtor::dtor_invoked, 0); test_int(CountNoDefaultCtor::copy_invoked, 0); test_int(CountNoDefaultCtor::move_invoked, 0); test_int(CountNoDefaultCtor::copy_ctor_invoked, 0); test_int(CountNoDefaultCtor::move_ctor_invoked, 0); CountNoDefaultCtor::reset(); auto e3 = world.entity().emplace(3); test_int(CountNoDefaultCtor::ctor_invoked, 1); test_int(CountNoDefaultCtor::dtor_invoked, 2); test_int(CountNoDefaultCtor::copy_invoked, 0); test_int(CountNoDefaultCtor::move_invoked, 0); test_int(CountNoDefaultCtor::copy_ctor_invoked, 0); test_int(CountNoDefaultCtor::move_ctor_invoked, 2); test_assert(e1.has()); test_assert(e2.has()); test_assert(e3.has()); test_int(e1.get()->value, 1); test_int(e2.get()->value, 2); test_int(e3.get()->value, 3); CountNoDefaultCtor::reset(); e1.add(); test_int(CountNoDefaultCtor::ctor_invoked, 0); test_int(CountNoDefaultCtor::dtor_invoked, 1); test_int(CountNoDefaultCtor::copy_invoked, 0); test_int(CountNoDefaultCtor::move_invoked, 1); test_int(CountNoDefaultCtor::copy_ctor_invoked, 0); test_int(CountNoDefaultCtor::move_ctor_invoked, 1); CountNoDefaultCtor::reset(); e2.add(); test_int(CountNoDefaultCtor::ctor_invoked, 0); test_int(CountNoDefaultCtor::dtor_invoked, 1); test_int(CountNoDefaultCtor::copy_invoked, 0); test_int(CountNoDefaultCtor::move_invoked, 0); test_int(CountNoDefaultCtor::copy_ctor_invoked, 0); test_int(CountNoDefaultCtor::move_ctor_invoked, 1); CountNoDefaultCtor::reset(); e3.add(); test_int(CountNoDefaultCtor::ctor_invoked, 0); test_int(CountNoDefaultCtor::dtor_invoked, 3); test_int(CountNoDefaultCtor::copy_invoked, 0); test_int(CountNoDefaultCtor::move_invoked, 0); test_int(CountNoDefaultCtor::copy_ctor_invoked, 0); test_int(CountNoDefaultCtor::move_ctor_invoked, 3); CountNoDefaultCtor::reset(); } test_int(CountNoDefaultCtor::ctor_invoked, 0); test_int(CountNoDefaultCtor::dtor_invoked, 3); test_int(CountNoDefaultCtor::copy_invoked, 0); test_int(CountNoDefaultCtor::move_invoked, 0); test_int(CountNoDefaultCtor::copy_ctor_invoked, 0); test_int(CountNoDefaultCtor::move_ctor_invoked, 0); } void ComponentLifecycle_grow_no_default_ctor_move_w_component(void) { { flecs::world world; world.component(); world.component(); auto e1 = world.entity().emplace(1); auto e2 = world.entity().emplace(2); test_int(CountNoDefaultCtor::ctor_invoked, 2); test_int(CountNoDefaultCtor::dtor_invoked, 0); test_int(CountNoDefaultCtor::copy_invoked, 0); test_int(CountNoDefaultCtor::move_invoked, 0); test_int(CountNoDefaultCtor::copy_ctor_invoked, 0); test_int(CountNoDefaultCtor::move_ctor_invoked, 0); CountNoDefaultCtor::reset(); auto e3 = world.entity().emplace(3); test_int(CountNoDefaultCtor::ctor_invoked, 1); test_int(CountNoDefaultCtor::dtor_invoked, 2); test_int(CountNoDefaultCtor::copy_invoked, 0); test_int(CountNoDefaultCtor::move_invoked, 0); test_int(CountNoDefaultCtor::copy_ctor_invoked, 0); test_int(CountNoDefaultCtor::move_ctor_invoked, 2); test_assert(e1.has()); test_assert(e2.has()); test_assert(e3.has()); test_int(e1.get()->value, 1); test_int(e2.get()->value, 2); test_int(e3.get()->value, 3); CountNoDefaultCtor::reset(); e1.add(); test_int(CountNoDefaultCtor::ctor_invoked, 0); test_int(CountNoDefaultCtor::dtor_invoked, 1); test_int(CountNoDefaultCtor::copy_invoked, 0); test_int(CountNoDefaultCtor::move_invoked, 1); test_int(CountNoDefaultCtor::copy_ctor_invoked, 0); test_int(CountNoDefaultCtor::move_ctor_invoked, 1); CountNoDefaultCtor::reset(); e2.add(); // e2 is last element, e3 got moved to e1's location test_int(CountNoDefaultCtor::ctor_invoked, 0); test_int(CountNoDefaultCtor::dtor_invoked, 1); test_int(CountNoDefaultCtor::copy_invoked, 0); test_int(CountNoDefaultCtor::move_invoked, 0); test_int(CountNoDefaultCtor::copy_ctor_invoked, 0); test_int(CountNoDefaultCtor::move_ctor_invoked, 1); CountNoDefaultCtor::reset(); e3.add(); test_int(CountNoDefaultCtor::ctor_invoked, 0); test_int(CountNoDefaultCtor::dtor_invoked, 3); /* 2 (e1,e2) for resize, 1 (e3) for moved away storage */ test_int(CountNoDefaultCtor::copy_invoked, 0); test_int(CountNoDefaultCtor::move_invoked, 0); test_int(CountNoDefaultCtor::copy_ctor_invoked, 0); test_int(CountNoDefaultCtor::move_ctor_invoked, 3); /* resize */ CountNoDefaultCtor::reset(); } test_int(CountNoDefaultCtor::ctor_invoked, 0); test_int(CountNoDefaultCtor::dtor_invoked, 3); test_int(CountNoDefaultCtor::copy_invoked, 0); test_int(CountNoDefaultCtor::move_invoked, 0); test_int(CountNoDefaultCtor::copy_ctor_invoked, 0); test_int(CountNoDefaultCtor::move_ctor_invoked, 0); } void ComponentLifecycle_delete_no_default_ctor(void) { { flecs::world world; world.component(); auto e1 = world.entity().emplace(1); auto e2 = world.entity().emplace(2); auto e3 = world.entity().emplace(3); test_int(CountNoDefaultCtor::ctor_invoked, 3); test_int(CountNoDefaultCtor::dtor_invoked, 2); test_int(CountNoDefaultCtor::copy_invoked, 0); test_int(CountNoDefaultCtor::move_invoked, 0); test_int(CountNoDefaultCtor::copy_ctor_invoked, 0); test_int(CountNoDefaultCtor::move_ctor_invoked, 2); test_assert(e1.has()); test_assert(e2.has()); test_assert(e3.has()); test_int(e1.get()->value, 1); test_int(e2.get()->value, 2); test_int(e3.get()->value, 3); e2.destruct(); test_int(CountNoDefaultCtor::ctor_invoked, 3); test_int(CountNoDefaultCtor::dtor_invoked, 3); test_int(CountNoDefaultCtor::copy_invoked, 0); test_int(CountNoDefaultCtor::move_invoked, 1); test_int(CountNoDefaultCtor::copy_ctor_invoked, 0); test_int(CountNoDefaultCtor::move_ctor_invoked, 2); } test_int(CountNoDefaultCtor::ctor_invoked, 3); test_int(CountNoDefaultCtor::dtor_invoked, 5); test_int(CountNoDefaultCtor::copy_invoked, 0); test_int(CountNoDefaultCtor::move_invoked, 1); test_int(CountNoDefaultCtor::copy_ctor_invoked, 0); test_int(CountNoDefaultCtor::move_ctor_invoked, 2); } void ComponentLifecycle_on_add_hook(void) { int count = 0; { flecs::world ecs; ecs.component().on_add([&](Position& p) { count ++; }); test_int(0, count); auto e = ecs.entity().add(); test_int(1, count); e.add(); test_int(1, count); } test_int(1, count); } void ComponentLifecycle_on_remove_hook(void) { int count = 0; { flecs::world ecs; ecs.component().on_remove([&](Position& p) { count ++; }); test_int(0, count); auto e1 = ecs.entity().add(); ecs.entity().add(); test_int(0, count); e1.remove(); test_int(1, count); } test_int(2, count); } void ComponentLifecycle_on_set_hook(void) { int count = 0; Position v = {0}; { flecs::world ecs; ecs.component().on_set([&](Position& p) { count ++; v = p; }); test_int(0, count); auto e1 = ecs.entity().add(); test_int(0, count); e1.set({10, 20}); test_int(1, count); test_int(10, v.x); test_int(20, v.y); ecs.entity().set({30, 40}); test_int(2, count); test_int(30, v.x); test_int(40, v.y); } test_int(2, count); } void ComponentLifecycle_on_add_hook_w_entity(void) { int count = 0; flecs::entity e_arg; { flecs::world ecs; ecs.component().on_add([&](flecs::entity arg, Position& p) { e_arg = arg; count ++; }); test_int(0, count); test_assert(e_arg == 0); auto e = ecs.entity().add(); test_int(1, count); test_assert(e_arg == e); e.add(); test_int(1, count); } test_int(1, count); } void ComponentLifecycle_on_remove_hook_w_entity(void) { int count = 0; flecs::entity e_arg; flecs::entity e2; { flecs::world ecs; ecs.component().on_remove([&](flecs::entity arg, Position& p){ e_arg = arg; count ++; }); test_int(0, count); test_assert(e_arg == 0); auto e1 = ecs.entity().add(); e2 = ecs.entity().add(); test_int(0, count); e1.remove(); test_int(1, count); test_assert(e_arg == e1); } test_int(2, count); test_assert(e_arg == e2); } void ComponentLifecycle_on_set_hook_w_entity(void) { int count = 0; Position v = {0}; flecs::entity e_arg; { flecs::world ecs; ecs.component().on_set([&](flecs::entity arg, Position& p) { count ++; v = p; e_arg = arg; }); test_int(0, count); auto e1 = ecs.entity().add(); test_int(0, count); e1.set({10, 20}); test_int(1, count); test_assert(e_arg == e1); test_int(10, v.x); test_int(20, v.y); auto e2 = ecs.entity().set({30, 40}); test_int(2, count); test_assert(e_arg == e2); test_int(30, v.x); test_int(40, v.y); } test_int(2, count); } void ComponentLifecycle_chained_hooks(void) { flecs::world ecs; int32_t add_count = 0; int32_t remove_count = 0; int32_t set_count = 0; ecs.component() .on_add([&](Position& p){ add_count ++; }) .on_set([&](Position& p){ set_count ++; }) .on_remove([&](Position& p){ remove_count ++; }); auto e = ecs.entity(); test_int(0, add_count); test_int(0, set_count); test_int(0, remove_count); e.add(); test_int(1, add_count); test_int(0, set_count); test_int(0, remove_count); e.set({10, 20}); test_int(1, add_count); test_int(1, set_count); test_int(0, remove_count); e.remove(); test_int(1, add_count); test_int(1, set_count); test_int(1, remove_count); } void ComponentLifecycle_ctor_w_2_worlds(void) { { flecs::world ecs; test_int(Pod::ctor_invoked, 0); ecs.entity().add(); test_int(Pod::ctor_invoked, 1); } Pod::ctor_invoked = 0; { flecs::world ecs; test_int(Pod::ctor_invoked, 0); ecs.entity().add(); test_int(Pod::ctor_invoked, 1); } } void ComponentLifecycle_ctor_w_2_worlds_explicit_registration(void) { { flecs::world ecs; ecs.component(); test_int(Pod::ctor_invoked, 0); ecs.entity().add(); test_int(Pod::ctor_invoked, 1); } Pod::ctor_invoked = 0; { flecs::world ecs; ecs.component(); test_int(Pod::ctor_invoked, 0); ecs.entity().add(); test_int(Pod::ctor_invoked, 1); } } struct DeferEmplaceTest { double x, y; DeferEmplaceTest(double x_, double y_) { x = x_; y = y_; } }; void ComponentLifecycle_defer_emplace(void) { flecs::world ecs; flecs::entity e = ecs.entity(); ecs.defer_begin(); e.emplace(10.0, 20.0); test_assert(!e.has()); ecs.defer_end(); test_assert(e.has()); const DeferEmplaceTest *p = e.get(); test_assert(p != NULL); test_int(p->x, 10); test_int(p->y, 20); } void ComponentLifecycle_emplace_w_on_add(void) { flecs::world ecs; flecs::entity e1 = ecs.entity(); int on_add = 0; ecs.component() .on_add([&](flecs::entity e, Position&) { on_add = true; test_assert(e == e1); }); e1.emplace(); test_int(on_add, 1); } void ComponentLifecycle_emplace_w_on_add_existing(void) { flecs::world ecs; flecs::entity e1 = ecs.entity().add(); int on_add = 0; ecs.component() .on_add([&](flecs::entity e, Position&) { on_add = true; test_assert(e == e1); }); e1.emplace(); test_int(on_add, 1); } void ComponentLifecycle_set_pair_no_copy(void) { flecs::world ecs; struct Tag { }; flecs::entity e = ecs.entity() .set({ 10 }); const NoCopy *ptr = e.get(); test_assert(ptr != NULL); test_int(ptr->x_, 10); } void ComponentLifecycle_set_pair_w_entity_no_copy(void) { flecs::world ecs; flecs::entity tag = ecs.entity(); flecs::entity e = ecs.entity() .set(tag, { 10 }); const NoCopy *ptr = e.get(tag); test_assert(ptr != NULL); test_int(ptr->x_, 10); } void ComponentLifecycle_set_pair_second_no_copy(void) { flecs::world ecs; flecs::entity tag = ecs.entity(); flecs::entity e = ecs.entity() .set_second(tag, { 10 }); const NoCopy *ptr = e.get_second(tag); test_assert(ptr != NULL); test_int(ptr->x_, 10); } void ComponentLifecycle_set_override_no_copy(void) { flecs::world ecs; flecs::entity e = ecs.entity() .set_override({ 10 }); const NoCopy *ptr = e.get(); test_assert(ptr != NULL); test_int(ptr->x_, 10); test_assert(e.has(flecs::Override | ecs.id())); } void ComponentLifecycle_set_override_pair_no_copy(void) { flecs::world ecs; flecs::entity e = ecs.entity() .set_override({ 10 }); const NoCopy *ptr = e.get(); test_assert(ptr != NULL); test_int(ptr->x_, 10); test_assert(e.has(flecs::Override | ecs.pair())); } void ComponentLifecycle_set_override_pair_w_entity_no_copy(void) { flecs::world ecs; flecs::entity tag = ecs.entity(); flecs::entity e = ecs.entity() .set_override(tag, { 10 }); const NoCopy *ptr = e.get(tag); test_assert(ptr != NULL); test_int(ptr->x_, 10); test_assert(e.has(flecs::Override | ecs.pair(tag))); } void ComponentLifecycle_dtor_after_defer_set(void) { { flecs::world ecs; auto e = ecs.entity(); ecs.defer_begin(); e.set({10}); test_assert(!e.has()); test_int(Pod::ctor_invoked, 2); test_int(Pod::dtor_invoked, 1); test_int(Pod::move_invoked, 1); test_int(Pod::move_ctor_invoked, 0); ecs.defer_end(); test_assert(e.has()); test_int(Pod::ctor_invoked, 3); test_int(Pod::dtor_invoked, 2); test_int(Pod::move_invoked, 2); test_int(Pod::move_ctor_invoked, 0); const Pod *ptr = e.get(); test_assert(ptr != NULL); test_int(ptr->value, 10); test_int(Pod::ctor_invoked, 3); test_int(Pod::dtor_invoked, 2); test_int(Pod::move_invoked, 2); test_int(Pod::move_ctor_invoked, 0); } test_int(Pod::ctor_invoked, 3); test_int(Pod::dtor_invoked, 3); test_int(Pod::move_invoked, 2); test_int(Pod::move_ctor_invoked, 0); } void ComponentLifecycle_dtor_with_relation(void) { { flecs::world ecs; auto e = ecs.entity(); auto e2 = ecs.entity().set({5}); e.set({10}).add(e2); test_int(Pod::ctor_invoked, 4); test_int(Pod::dtor_invoked, 3); test_int(Pod::move_invoked, 2); test_int(Pod::move_ctor_invoked, 1); const Pod *ptr = e.get(); test_assert(ptr != NULL); test_int(ptr->value, 10); test_int(Pod::ctor_invoked, 4); test_int(Pod::dtor_invoked, 3); test_int(Pod::move_invoked, 2); test_int(Pod::move_ctor_invoked, 1); } test_int(Pod::ctor_invoked, 5); test_int(Pod::dtor_invoked, 6); test_int(Pod::move_invoked, 4); test_int(Pod::move_ctor_invoked, 1); } void ComponentLifecycle_register_parent_after_child_w_hooks(void) { { flecs::world ecs; ecs.component(); ecs.component(); ecs.entity().set({}); } test_int(Pod::ctor_invoked, 2); test_int(Pod::dtor_invoked, 2); test_int(Pod::move_invoked, 1); test_int(Pod::move_ctor_invoked, 0); test_int(Pod::copy_invoked, 0); test_int(Pod::copy_ctor_invoked, 0); } void ComponentLifecycle_register_parent_after_child_w_hooks_implicit(void) { { flecs::world ecs; ecs.entity().add().set({}); } test_int(Pod::ctor_invoked, 2); test_int(Pod::dtor_invoked, 2); test_int(Pod::move_invoked, 1); test_int(Pod::move_ctor_invoked, 0); test_int(Pod::copy_invoked, 0); test_int(Pod::copy_ctor_invoked, 0); }