diff --git a/distr/flecs.c b/distr/flecs.c index 7edf94ae7..98393f1b4 100644 --- a/distr/flecs.c +++ b/distr/flecs.c @@ -37041,7 +37041,7 @@ void ecs_table_clear_entities( bool notify, bool deallocate) { - flecs_table_fini_data(world, table, notify, false, true, deallocate); + flecs_table_fini_data(world, table, notify, true, true, deallocate); } /* Cleanup, no OnRemove, clear entity index, deactivate table, free allocations */ diff --git a/src/storage/table.c b/src/storage/table.c index 90e542c08..e526b3844 100644 --- a/src/storage/table.c +++ b/src/storage/table.c @@ -963,7 +963,7 @@ void ecs_table_clear_entities( bool notify, bool deallocate) { - flecs_table_fini_data(world, table, notify, false, true, deallocate); + flecs_table_fini_data(world, table, notify, true, true, deallocate); } /* Cleanup, no OnRemove, clear entity index, deactivate table, free allocations */ diff --git a/test/core/project.json b/test/core/project.json index ea2866f71..d11691180 100644 --- a/test/core/project.json +++ b/test/core/project.json @@ -2296,7 +2296,13 @@ "has_id", "has_pair", "has_wildcard_pair", - "has_any_pair" + "has_any_pair", + "clear_table_kills_entities", + "clear_table_add_new", + "clear_table_check_size", + "clear_table_twice_check_size", + "clear_table_on_remove_hooks", + "clear_table_on_remove_observer" ] }, { "id": "Poly", diff --git a/test/core/src/Table.c b/test/core/src/Table.c index e8a2a76b7..9dd17f723 100644 --- a/test/core/src/Table.c +++ b/test/core/src/Table.c @@ -617,3 +617,228 @@ void Table_has_any_pair(void) { ecs_fini(world); } + +static +void Clear_table_and_verify_members_not_alive(bool notify, bool dealloc) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e1 = ecs_new(world); + ecs_add(world, e1, Position); + ecs_entity_t e2 = ecs_new(world); + ecs_add(world, e2, Position); + + ecs_table_t *table = ecs_get_table(world, e1); + + ecs_table_clear_entities(world, table, notify, dealloc); + + test_assert(!ecs_is_alive(world, e1)); + test_assert(!ecs_is_alive(world, e2)); + test_int(0, ecs_table_count(table)); + + ecs_fini(world); +} + +void Table_clear_table_kills_entities(void) { + Clear_table_and_verify_members_not_alive(true, true); + Clear_table_and_verify_members_not_alive(false, true); + Clear_table_and_verify_members_not_alive(true, false); + Clear_table_and_verify_members_not_alive(false, false); +} + +static +void Clear_table_and_verify_new_members_exist(bool notify, bool dealloc) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t ecs_id(Position) = ecs_component(world, { + .entity = ecs_new(world), + .type.size = ECS_SIZEOF(Position), + .type.alignment = ECS_ALIGNOF(Position) + }); + + ecs_entity_t e1 = ecs_new(world); + ecs_add(world, e1, Position); + ecs_entity_t e2 = ecs_new(world); + ecs_add(world, e2, Position); + + ecs_table_t *table = ecs_get_table(world, e1); + + ecs_table_clear_entities(world, table, notify, dealloc); + + ecs_id(Position) = ecs_component(world, { + .entity = ecs_new(world), + .type.size = ECS_SIZEOF(Position), + .type.alignment = ECS_ALIGNOF(Position) + }); + + ecs_entity_t e3 = ecs_new(world); + ecs_add(world, e3, Position); + ecs_entity_t e4 = ecs_new(world); + ecs_add(world, e4, Position); + test_assert(ecs_is_alive(world, e3)); + test_assert(ecs_is_alive(world, e4)); + + ecs_table_t *table_e3 = ecs_get_table(world, e3); + ecs_table_t *table_e4 = ecs_get_table(world, e4); + + test_assert(table_e3 == table_e4); + test_int(2, ecs_table_count(table_e3)); + + ecs_fini(world); +} + +void Table_clear_table_add_new(void) { + Clear_table_and_verify_new_members_exist(true, true); + Clear_table_and_verify_new_members_exist(false, true); + Clear_table_and_verify_new_members_exist(true, false); + Clear_table_and_verify_new_members_exist(false, false); +} + +static +void Clear_table_and_check_size(bool notify, bool dealloc) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e1 = ecs_new(world); + ecs_add(world, e1, Position); + ecs_entity_t e2 = ecs_new(world); + ecs_add(world, e2, Position); + + ecs_table_t *table = ecs_get_table(world, e1); + const int32_t size = ecs_table_size(table); + + ecs_table_clear_entities(world, table, notify, dealloc); + + const int32_t expected_size = dealloc ? 0 : size; + test_int(expected_size, ecs_table_size(table)); + test_int(0, ecs_table_count(table)); + + ecs_fini(world); +} + +void Table_clear_table_check_size(void) { + Clear_table_and_check_size(true, true); + Clear_table_and_check_size(false, true); + Clear_table_and_check_size(true, false); + Clear_table_and_check_size(false, false); +} + +static +void Clear_table_twice_and_check_size(bool first_dealloc, bool second_dealloc) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e1 = ecs_new(world); + ecs_add(world, e1, Position); + ecs_entity_t e2 = ecs_new(world); + ecs_add(world, e2, Position); + + ecs_table_t *table = ecs_get_table(world, e1); + const int32_t size = ecs_table_size(table); + + ecs_table_clear_entities(world, table, true, first_dealloc); + ecs_table_clear_entities(world, table, true, second_dealloc); + + const int32_t expected_size = (first_dealloc || second_dealloc) ? 0 : size; + test_int(expected_size, ecs_table_size(table)); + test_int(0, ecs_table_count(table)); + + ecs_fini(world); +} + +void Table_clear_table_twice_check_size(void) { + Clear_table_twice_and_check_size(true, true); + Clear_table_twice_and_check_size(false, true); + Clear_table_twice_and_check_size(true, false); + Clear_table_twice_and_check_size(false, false); +} + +static int on_remove_position = 0; + +static void ecs_on_remove(Position)(ecs_iter_t *it) { + test_assert(it->count >= 1); + test_assert(it->event == EcsOnRemove); + + Position *p = ecs_field(it, Position, 0); + for (int i = 0; i < it->count; i ++) { + on_remove_position ++; + test_int(p[i].x, 10); + test_int(p[i].y, 20); + } +} + +static +void Verify_on_remove_hooks_table_clear(bool notify, bool dealloc) { + ecs_world_t *world = ecs_mini(); + on_remove_position = 0; + + ECS_COMPONENT(world, Position); + + ecs_set_hooks(world, Position, { + .on_remove = ecs_on_remove(Position) + }); + + ecs_entity_t e1 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e2 = ecs_insert(world, ecs_value(Position, {10, 20})); + ecs_entity_t e3 = ecs_insert(world, ecs_value(Position, {10, 20})); + + ecs_table_t *table = ecs_get_table(world, e1); + ecs_table_clear_entities(world, table, notify, dealloc); + + test_int(on_remove_position, 3); + + ecs_fini(world); +} + +void Table_clear_table_on_remove_hooks(void) { + Verify_on_remove_hooks_table_clear(true, true); + Verify_on_remove_hooks_table_clear(false, true); + Verify_on_remove_hooks_table_clear(true, false); + Verify_on_remove_hooks_table_clear(false, false); +} + +static +void Observer(ecs_iter_t *it) { + probe_system_w_ctx(it, it->ctx); +} + +static +void Check_on_remove_when_clearing_table(bool notify, bool dealloc) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + Probe ctx = {0}; + ecs_entity_t o = ecs_observer(world, { + .query.terms = {{EcsWildcard}}, + .events = {EcsOnRemove}, + .callback = Observer, + .ctx = &ctx + }); + test_assert(o != 0); + + ecs_entity_t e1 = ecs_new(world); + ecs_add(world, e1, Position); + ecs_entity_t e2 = ecs_new(world); + ecs_add(world, e2, Position); + + test_int(ctx.invoked, 0); + + ecs_table_t *table = ecs_get_table(world, e1); + ecs_table_clear_entities(world, table, notify, dealloc); + + test_int(ctx.invoked, notify ? 1 : 0); + test_int(ctx.count, notify ? 2 : 0); + + ecs_fini(world); +} + +void Table_clear_table_on_remove_observer(void) { + Check_on_remove_when_clearing_table(true, true); + Check_on_remove_when_clearing_table(false, true); + Check_on_remove_when_clearing_table(true, false); + Check_on_remove_when_clearing_table(false, false); +} diff --git a/test/core/src/main.c b/test/core/src/main.c index 531c22347..a9568bebb 100644 --- a/test/core/src/main.c +++ b/test/core/src/main.c @@ -2210,6 +2210,12 @@ void Table_has_id(void); void Table_has_pair(void); void Table_has_wildcard_pair(void); void Table_has_any_pair(void); +void Table_clear_table_kills_entities(void); +void Table_clear_table_add_new(void); +void Table_clear_table_check_size(void); +void Table_clear_table_twice_check_size(void); +void Table_clear_table_on_remove_hooks(void); +void Table_clear_table_on_remove_observer(void); // Testsuite 'Poly' void Poly_on_set_poly_observer(void); @@ -10803,6 +10809,30 @@ bake_test_case Table_testcases[] = { { "has_any_pair", Table_has_any_pair + }, + { + "clear_table_kills_entities", + Table_clear_table_kills_entities + }, + { + "clear_table_add_new", + Table_clear_table_add_new + }, + { + "clear_table_check_size", + Table_clear_table_check_size + }, + { + "clear_table_twice_check_size", + Table_clear_table_twice_check_size + }, + { + "clear_table_on_remove_hooks", + Table_clear_table_on_remove_hooks + }, + { + "clear_table_on_remove_observer", + Table_clear_table_on_remove_observer } }; @@ -11251,7 +11281,7 @@ static bake_test_suite suites[] = { "Table", NULL, NULL, - 24, + 30, Table_testcases }, {