Files
PixelDefense/engine/libs/flecs/test/addons/src/SystemMisc.c

1779 lines
43 KiB
C

#include <addons.h>
static
int32_t dummy_invoked = false;
static
void Dummy(ecs_iter_t *it) {
dummy_invoked = true;
probe_iter(it);
}
void SystemMisc_invalid_not_without_id(void) {
install_test_abort();
ecs_world_t *world = ecs_init();
test_expect_abort();
ECS_SYSTEM(world, Dummy, EcsOnUpdate, !);
ecs_fini(world);
}
void SystemMisc_invalid_optional_without_id(void) {
install_test_abort();
ecs_world_t *world = ecs_init();
test_expect_abort();
ECS_SYSTEM(world, Dummy, EcsOnUpdate, ?);
ecs_fini(world);
}
void SystemMisc_invalid_entity_without_id(void) {
install_test_abort();
ecs_world_t *world = ecs_init();
ECS_ENTITY(world, Foo, 0);
test_expect_abort();
ECS_SYSTEM(world, Dummy, EcsOnUpdate, FOO.);
ecs_fini(world);
}
void SystemMisc_invalid_empty_without_id(void) {
install_test_abort();
ecs_world_t *world = ecs_init();
test_expect_abort();
ECS_SYSTEM(world, Dummy, EcsOnUpdate, $This);
test_assert(true);
ecs_fini(world);
}
void SystemMisc_invalid_empty_element(void) {
install_test_abort();
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
test_expect_abort();
ECS_SYSTEM(world, Dummy, EcsOnUpdate,, Position);
ecs_fini(world);
}
void SystemMisc_invalid_empty_element_w_space(void) {
install_test_abort();
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
test_expect_abort();
ECS_SYSTEM(world, Dummy, EcsOnUpdate, , Position);
ecs_fini(world);
}
void SystemMisc_invalid_empty_or(void) {
install_test_abort();
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
test_expect_abort();
ECS_SYSTEM(world, Dummy, EcsOnUpdate, || Position);
ecs_fini(world);
}
void SystemMisc_invalid_empty_or_w_space(void) {
install_test_abort();
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
test_expect_abort();
ECS_SYSTEM(world, Dummy, EcsOnUpdate,|| Position);
ecs_fini(world);
}
void SystemMisc_invalid_or_w_not(void) {
install_test_abort();
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
test_expect_abort();
ECS_SYSTEM(world, Dummy, EcsOnUpdate, Position || !Velocity);
ecs_fini(world);
}
void SystemMisc_invalid_not_w_or(void) {
install_test_abort();
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
test_expect_abort();
ECS_SYSTEM(world, Dummy, EcsOnUpdate, !Position || Velocity);
ecs_fini(world);
}
void SystemMisc_invalid_0_w_and(void) {
install_test_abort();
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
test_expect_abort();
ECS_SYSTEM(world, Dummy, EcsOnUpdate, 0, Position);
ecs_fini(world);
}
void SystemMisc_invalid_0_w_from_entity(void) {
install_test_abort();
ecs_world_t *world = ecs_init();
ECS_ENTITY(world, Foo, 0);
test_expect_abort();
ECS_SYSTEM(world, Dummy, EcsOnUpdate, Foo.0);
ecs_fini(world);
}
void SystemMisc_invalid_component_id(void) {
install_test_abort();
ecs_world_t *world = ecs_init();
test_expect_abort();
ECS_SYSTEM(world, Dummy, EcsOnUpdate, Position);
ecs_fini(world);
}
void SystemMisc_invalid_entity_id(void) {
install_test_abort();
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
test_expect_abort();
ECS_SYSTEM(world, Dummy, EcsOnUpdate, Position(NotValid));
ecs_fini(world);
}
void SystemMisc_invalid_null_string(void) {
ecs_world_t *world = ecs_init();
ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "Dummy", .add = {ecs_dependson(EcsOnUpdate)}}),
.callback = Dummy
});
ecs_progress(world, 0);
test_assert(dummy_invoked == true);
ecs_fini(world);
}
void SystemMisc_invalid_empty_string(void) {
ecs_world_t *world = ecs_init();
ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "Dummy", .add = {ecs_dependson(EcsOnUpdate)}}),
.query.filter.expr = "",
.callback = Dummy
});
ecs_progress(world, 0);
test_assert(dummy_invoked == true);
ecs_fini(world);
}
void SystemMisc_invalid_empty_string_w_space(void) {
ecs_world_t *world = ecs_init();
ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "Dummy", .add = {ecs_dependson(EcsOnUpdate)}}),
.query.filter.expr = " ",
.callback = Dummy
});
ecs_progress(world, 0);
test_assert(dummy_invoked == true);
ecs_fini(world);
}
void SystemMisc_redefine_row_system(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ecs_entity_t s;
{
ECS_OBSERVER(world, Dummy, EcsOnAdd, Position);
s = Dummy;
}
ECS_OBSERVER(world, Dummy, EcsOnAdd, Position);
test_assert(s == Dummy);
ecs_fini(world);
}
static int is_invoked;
static
void IsInvoked(ecs_iter_t *it) {
is_invoked ++;
}
void SystemMisc_system_w_or_prefab(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_PREFAB(world, Prefab, Position);
ECS_SYSTEM(world, IsInvoked, EcsOnUpdate, Position, flecs.core.Prefab || Disabled);
test_int(is_invoked, 0);
ecs_progress(world, 1);
test_int(is_invoked, 1);
ecs_fini(world);
}
void SystemMisc_system_w_or_disabled(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_ENTITY(world, Entity, Position, Disabled);
ECS_SYSTEM(world, IsInvoked, EcsOnUpdate, Position, Prefab || Disabled);
test_int(is_invoked, 0);
ecs_progress(world, 1);
test_int(is_invoked, 1);
ecs_fini(world);
}
void SystemMisc_system_w_or_disabled_and_prefab(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_PREFAB(world, Prefab, Position);
ECS_ENTITY(world, Entity, Position, Disabled);
ECS_SYSTEM(world, IsInvoked, EcsOnUpdate, Position, flecs.core.Prefab || Disabled);
test_int(is_invoked, 0);
ecs_progress(world, 1);
test_int(is_invoked, 2);
is_invoked = false;
ecs_fini(world);
}
static
void TableColumns(ecs_iter_t *it) {
Position *p = ecs_field(it, Position, 1);
Velocity *v = ecs_field(it, Velocity, 2);
ecs_id_t ecs_id(Position) = ecs_field_id(it, 1);
ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 2);
const ecs_type_t *type = ecs_table_get_type(it->table);
test_int(2, type->count);
ecs_entity_t *components = type->array;
test_int(components[0], ecs_id(Position));
test_int(components[1], ecs_id(Velocity));
void *column_0 = ecs_table_get(it->real_world, it->table, Position, 0);
test_assert(column_0 == p);
void *column_1 = ecs_table_get(it->real_world, it->table, Velocity, 0);
test_assert(column_1 == v);
is_invoked ++;
}
void SystemMisc_table_columns_access(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ecs_entity_t e = ecs_new(world, 0);
ecs_add(world, e, Position);
ecs_add(world, e, Velocity);
ECS_SYSTEM(world, TableColumns, EcsOnUpdate, Position, Velocity);
test_int(is_invoked, 0);
ecs_progress(world, 1);
test_int(is_invoked, 1);
is_invoked = false;
ecs_fini(world);
}
void SystemMisc_dont_enable_after_rematch(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ECS_SYSTEM(world, Dummy, EcsOnUpdate, Position(self|up), Velocity(self|up));
/* Create an entity that is watched. Whenever components are added/removed
* to and/or from watched entities, a rematch is triggered. */
ECS_PREFAB(world, Prefab, Position);
ECS_ENTITY(world, Entity, (IsA, Prefab));
Probe ctx = {0};
ecs_set_ctx(world, &ctx, NULL);
/* System is enabled but doesn't match with any entities */
test_bool(ecs_has_id(world, Dummy, EcsDisabled), false);
ecs_progress(world, 1);
test_int(ctx.count, 0);
/* Explicitly disable system before triggering a rematch */
ecs_enable(world, Dummy, false);
test_bool(ecs_has_id(world, Dummy, EcsDisabled), true);
/* Trigger a rematch. System should still be disabled after this */
ecs_add(world, Prefab, Velocity);
test_bool(ecs_has_id(world, Dummy, EcsDisabled), true);
ecs_progress(world, 1);
test_int(ctx.count, 0);
/* Enable system. It is matched, so should now be invoked */
ecs_enable(world, Dummy, true);
test_bool(ecs_has_id(world, Dummy, EcsDisabled), false);
ecs_progress(world, 1);
test_int(ctx.count, 1);
ecs_fini(world);
}
static void SysA(ecs_iter_t *it)
{
ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 2);
ecs_add(it->world, it->entities[0], Velocity);
}
static int b_invoked;
static ecs_entity_t b_entity;
static void SysB(ecs_iter_t *it)
{
b_invoked ++;
b_entity = it->entities[0];
}
void SystemMisc_ensure_single_merge(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ECS_SYSTEM(world, SysA, EcsOnLoad, Position, [out] !Velocity);
ECS_SYSTEM(world, SysB, EcsPostLoad, Velocity);
ECS_ENTITY(world, MyEntity, Position);
ecs_progress(world, 0);
test_assert(b_invoked == 1);
ecs_fini(world);
}
static int test_table_count_invoked;
static void TestTableCount(ecs_iter_t *it) {
test_int(it->table_count, 2);
test_table_count_invoked ++;
}
void SystemMisc_table_count(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ECS_COMPONENT(world, Mass);
ECS_SYSTEM(world, TestTableCount, EcsOnUpdate, Position);
// Create two active tables
ecs_new(world, Position);
ecs_entity_t e2 = ecs_new(world, Position);
ecs_add(world, e2, Velocity);
// Create one inactive table
ecs_entity_t e3 = ecs_new(world, Position);
ecs_add(world, e3, Velocity);
ecs_add(world, e3, Mass);
ecs_delete(world, e3);
ecs_progress(world, 0);
test_int(test_table_count_invoked, 2);
ecs_fini(world);
}
void SystemMisc_match_system(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ECS_SYSTEM(world, SysA, 0, Position(SysA));
ECS_SYSTEM(world, SysB, 0, Position);
ecs_run(world, SysB, 0, NULL);
test_assert(b_invoked != 0);
test_assert(b_entity == SysA);
ecs_fini(world);
}
void SystemMisc_system_initial_state(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_SYSTEM(world, SysA, 0, Position);
test_assert( ecs_has_id(world, SysA, EcsEmpty));
test_assert( !ecs_has_id(world, SysA, EcsDisabled));
ecs_fini(world);
}
static
void FooSystem(ecs_iter_t *it) { }
static
void BarSystem(ecs_iter_t *it) { }
void SystemMisc_add_own_component(void) {
ecs_world_t * world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_SYSTEM(world, FooSystem, 0, Position);
ECS_SYSTEM(world, BarSystem, 0, Position);
ecs_set_ptr(world, BarSystem, Position, NULL);
/* Make sure code didn't assert */
test_assert(true);
ecs_fini(world);
}
static bool action_a_invoked;
static bool action_b_invoked;
static
void ActionA(ecs_iter_t *it) {
action_a_invoked = true;
}
static
void ActionB(ecs_iter_t *it) {
action_b_invoked = true;
}
void SystemMisc_change_system_action(void) {
ecs_world_t * world = ecs_init();
ECS_COMPONENT(world, Position);
ecs_entity_t sys = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "Sys", .add = {ecs_dependson(EcsOnUpdate)}}),
.query.filter.expr = "Position",
.callback = ActionA
});
ecs_new(world, Position);
test_bool(action_a_invoked, false);
test_bool(action_b_invoked, false);
ecs_progress(world, 0);
test_bool(action_a_invoked, true);
test_bool(action_b_invoked, false);
action_a_invoked = false;
ecs_system_init(world, &(ecs_system_desc_t){
.entity = sys,
.callback = ActionB
});
ecs_progress(world, 0);
test_bool(action_a_invoked, false);
test_bool(action_b_invoked, true);
ecs_fini(world);
}
void SystemMisc_system_readeactivate(void) {
ecs_world_t * world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_SYSTEM(world, Dummy, EcsOnUpdate, Position);
/* No entities, system should be deactivated */
test_assert( ecs_has_id(world, Dummy, EcsEmpty));
ecs_entity_t e = ecs_new(world, Position);
ecs_run_aperiodic(world, 0);
/* System should be active, one entity is matched */
test_assert( !ecs_has_id(world, Dummy, EcsEmpty));
ecs_delete(world, e);
ecs_run_aperiodic(world, 0);
/* System is not automatically deactivated */
test_assert( !ecs_has_id(world, Dummy, EcsEmpty));
/* Manually deactivate system that aren't matched with entities */
ecs_run_aperiodic(world, EcsAperiodicEmptyQueries);
/* System should be deactivated */
test_assert( ecs_has_id(world, Dummy, EcsEmpty));
ecs_fini(world);
}
static
void Dummy1(ecs_iter_t *it) { }
static
void Dummy2(ecs_iter_t *it) { }
void SystemMisc_system_readeactivate_w_2_systems(void) {
ecs_world_t * world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Mass);
ECS_SYSTEM(world, Dummy1, EcsOnUpdate, Position);
ECS_SYSTEM(world, Dummy2, EcsOnUpdate, Mass);
/* No entities, system should be deactivated */
test_assert( ecs_has_id(world, Dummy1, EcsEmpty));
test_assert( ecs_has_id(world, Dummy2, EcsEmpty));
ecs_entity_t e1 = ecs_new(world, Position);
ecs_new(world, Mass);
ecs_run_aperiodic(world, 0);
/* Systems should be active, one entity is matched */
test_assert( !ecs_has_id(world, Dummy1, EcsEmpty));
test_assert( !ecs_has_id(world, Dummy2, EcsEmpty));
ecs_delete(world, e1);
ecs_run_aperiodic(world, 0);
/* System is not automatically deactivated */
test_assert( !ecs_has_id(world, Dummy1, EcsEmpty));
test_assert( !ecs_has_id(world, Dummy2, EcsEmpty));
/* Manually deactivate system that aren't matched with entities */
ecs_run_aperiodic(world, EcsAperiodicEmptyQueries);
/* System should be deactivated */
test_assert( ecs_has_id(world, Dummy1, EcsEmpty));
test_assert( !ecs_has_id(world, Dummy2, EcsEmpty));
ecs_fini(world);
}
void SystemMisc_add_to_system_in_progress(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_TAG(world, Tag);
ECS_SYSTEM(world, Dummy, EcsOnUpdate, Position);
ecs_new(world, Position);
ecs_defer_begin(world);
ecs_add(world, Dummy, Tag);
ecs_defer_end(world);
ecs_progress(world, 0);
test_assert(dummy_invoked == true);
ecs_fini(world);
}
static
void Action(ecs_iter_t *it) { }
void SystemMisc_redefine_null_signature(void) {
ecs_world_t *world = ecs_init();
ecs_entity_t s_1 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "System", .add = {ecs_dependson(EcsOnUpdate)}}),
.query.filter.expr = NULL,
.callback = Action
});
ecs_entity_t s_2 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "System", .add = {ecs_dependson(EcsOnUpdate)}}),
.query.filter.expr = NULL,
.callback = Action
});
test_assert(s_1 == s_2);
ecs_fini(world);
}
void SystemMisc_redefine_0_signature(void) {
ecs_world_t *world = ecs_init();
ecs_entity_t s_1 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "System", .add = {ecs_dependson(EcsOnUpdate)}}),
.query.filter.expr = "0",
.callback = Action
});
ecs_entity_t s_2 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "System", .add = {ecs_dependson(EcsOnUpdate)}}),
.query.filter.expr = "0",
.callback = Action
});
test_assert(s_1 == s_2);
ecs_fini(world);
}
void SystemMisc_one_named_column_of_two(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ecs_filter_t f = ECS_FILTER_INIT;
test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){
.storage = &f, .terms = {
{ ecs_id(Position), .name = "pos" },
{ ecs_id(Velocity) }
}}));
test_int(f.term_count, 2);
test_int(f.field_count, 2);
ecs_term_t *
term = &f.terms[0];
test_assert(term->oper == EcsAnd);
test_assert(term->src.flags == (EcsSelf|EcsUp|EcsIsVariable));
test_assert(term->src.id == EcsThis);
test_assert(term->inout == EcsInOutDefault);
test_assert(term->id == ecs_id(Position));
test_str(term->name, "pos");
term = &f.terms[1];
test_assert(term->oper == EcsAnd);
test_assert(term->src.flags == (EcsSelf|EcsUp|EcsIsVariable));
test_assert(term->src.id == EcsThis);
test_assert(term->inout == EcsInOutDefault);
test_assert(term->id == ecs_id(Velocity));
test_str(term->name, NULL);
ecs_filter_fini(&f);
ecs_fini(world);
}
void SystemMisc_two_named_columns_of_two(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ecs_filter_t f = ECS_FILTER_INIT;
test_assert(NULL != ecs_filter_init(world, &(ecs_filter_desc_t){
.storage = &f, .terms = {
{ ecs_id(Position), .name = "pos" },
{ ecs_id(Velocity), .name = "vel" }
}}));
test_int(f.term_count, 2);
test_int(f.field_count, 2);
ecs_term_t *
term = &f.terms[0];
test_assert(term->oper == EcsAnd);
test_assert(term->src.flags == (EcsSelf|EcsUp|EcsIsVariable));
test_assert(term->src.id == EcsThis);
test_assert(term->inout == EcsInOutDefault);
test_assert(term->id == ecs_id(Position));
test_str(term->name, "pos");
term = &f.terms[1];
test_assert(term->oper == EcsAnd);
test_assert(term->src.flags == (EcsSelf|EcsUp|EcsIsVariable));
test_assert(term->src.id == EcsThis);
test_assert(term->inout == EcsInOutDefault);
test_assert(term->id == ecs_id(Velocity));
test_str(term->name, "vel");
ecs_filter_fini(&f);
ecs_fini(world);
}
void SystemMisc_redeclare_system_explicit_id(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ecs_entity_t s1 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, {.name = "Move", .add = {ecs_dependson(EcsOnUpdate)}}),
.query.filter.expr = "Position, Velocity",
.callback = Dummy
});
ecs_entity_t s2 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, {.id = s1, .name = "Move", .add = {ecs_dependson(EcsOnUpdate)}}),
.query.filter.expr = "Position, Velocity",
.callback = Dummy
});
test_assert(s1 == s2);
ecs_fini(world);
}
void SystemMisc_redeclare_system_explicit_id_null_expr(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ecs_entity_t s1 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, {.name = "Move", .add = {ecs_dependson(EcsOnUpdate)}}),
.query.filter.expr = NULL,
.callback = Dummy
});
ecs_entity_t s2 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, {.id = s1, .name = "Move", .add = {ecs_dependson(EcsOnUpdate)}}),
.query.filter.expr = NULL,
.callback = Dummy
});
test_assert(s1 == s2);
ecs_fini(world);
}
void SystemMisc_redeclare_system_explicit_id_no_name(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ecs_entity_t s1 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, {.name = "Move", .add = {ecs_dependson(EcsOnUpdate)}}),
.query.filter.expr = "Position, Velocity",
.callback = Dummy
});
ecs_entity_t s2 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, {.id = s1, .add = {ecs_dependson(EcsOnUpdate)}}),
.query.filter.expr = "Position, Velocity",
.callback = Dummy
});
test_assert(s1 == s2);
test_str(ecs_get_name(world, s1), "Move");
ecs_fini(world);
}
void SystemMisc_declare_different_id_same_name(void) {
install_test_abort();
ecs_world_t *world = ecs_init();
ecs_entity_t e1 = ecs_new(world, 0);
ecs_entity_t e2 = ecs_new(world, 0);
ecs_entity_t s_1 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, {.id = e1, .name = "Move", .add = {ecs_dependson(EcsOnUpdate)}}),
.query.filter.expr = "0",
.callback = Dummy
});
test_assert(e1 == s_1);
test_expect_abort();
ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, {.id = e2, .name = "Move", .add = {ecs_dependson(EcsOnUpdate)}}),
.query.filter.expr = "0",
.callback = Dummy
});
}
void SystemMisc_declare_different_id_same_name_w_scope(void) {
install_test_abort();
ecs_world_t *world = ecs_init();
ecs_entity_t scope = ecs_new(world, 0);
ecs_set_scope(world, scope);
ecs_entity_t e1 = ecs_new(world, 0);
ecs_entity_t e2 = ecs_new(world, 0);
ecs_entity_t s_1 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, {.id = e1, .name = "Move", .add = {ecs_dependson(EcsOnUpdate)}}),
.query.filter.expr = "0",
.callback = Dummy
});
test_assert(e1 == s_1);
test_expect_abort();
ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, {.id = e2, .name = "Move", .add = {ecs_dependson(EcsOnUpdate)}}),
.query.filter.expr = "0",
.callback = Dummy
});
}
void SystemMisc_rw_in_implicit_any(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ecs_query_t *q = ecs_query_new(world, "Position, Velocity(self|up)");
ecs_entity_t e = ecs_new(world, Position);
ecs_add(world, e, Velocity);
ecs_iter_t it = ecs_query_iter(world, q);
test_assert(ecs_query_next(&it) == true);
test_assert(ecs_field_is_readonly(&it, 1) == false);
test_assert(ecs_field_is_readonly(&it, 2) == false);
test_assert(ecs_query_next(&it) == false);
ecs_fini(world);
}
void SystemMisc_rw_in_implicit_shared(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ecs_query_t *q = ecs_query_new(world, "Position, Velocity(up)");
ecs_entity_t base = ecs_new(world, Velocity);
ecs_entity_t e = ecs_new(world, Position);
ecs_add_pair(world, e, EcsIsA, base);
ecs_iter_t it = ecs_query_iter(world, q);
test_assert(ecs_query_next(&it) == true);
test_assert(ecs_field_is_readonly(&it, 1) == false);
test_assert(ecs_field_is_readonly(&it, 2) == true);
test_assert(ecs_query_next(&it) == false);
ecs_fini(world);
}
void SystemMisc_rw_in_implicit_from_empty(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ecs_query_t *q = ecs_query_new(world, "Position, Velocity()");
ecs_entity_t e = ecs_new(world, Position);
ecs_add(world, e, Velocity);
ecs_iter_t it = ecs_query_iter(world, q);
test_assert(ecs_query_next(&it) == true);
test_assert(ecs_field_is_readonly(&it, 1) == false);
test_assert(ecs_field_is_readonly(&it, 2) == true);
test_assert(ecs_query_next(&it) == false);
ecs_fini(world);
}
void SystemMisc_rw_in_implicit_from_entity(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ECS_ENTITY(world, f, Velocity);
ecs_query_t *q = ecs_query_new(world, "Position, Velocity(f)");
ecs_entity_t e = ecs_new(world, Position);
ecs_add(world, e, Velocity);
ecs_iter_t it = ecs_query_iter(world, q);
test_assert(ecs_query_next(&it) == true);
test_assert(ecs_field_is_readonly(&it, 1) == false);
test_assert(ecs_field_is_readonly(&it, 2) == true);
test_assert(ecs_query_next(&it) == false);
ecs_fini(world);
}
void SystemMisc_rw_out_explicit_any(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ecs_query_t *q = ecs_query_new(world, "Position, [out] Velocity(self|up)");
ecs_entity_t e = ecs_new(world, Position);
ecs_add(world, e, Velocity);
ecs_iter_t it = ecs_query_iter(world, q);
test_assert(ecs_query_next(&it) == true);
test_assert(ecs_field_is_readonly(&it, 1) == false);
test_assert(ecs_field_is_readonly(&it, 2) == false);
test_assert(ecs_query_next(&it) == false);
ecs_fini(world);
}
void SystemMisc_rw_out_explicit_shared(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ecs_query_t *q = ecs_query_new(world, "Position, [out] Velocity(up)");
ecs_entity_t base = ecs_new(world, Velocity);
ecs_entity_t e = ecs_new(world, Position);
ecs_add_pair(world, e, EcsIsA, base);
ecs_iter_t it = ecs_query_iter(world, q);
test_assert(ecs_query_next(&it) == true);
test_assert(ecs_field_is_readonly(&it, 1) == false);
test_assert(ecs_field_is_readonly(&it, 2) == false);
test_assert(ecs_query_next(&it) == false);
ecs_fini(world);
}
void SystemMisc_rw_out_explicit_from_empty(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ecs_query_t *q = ecs_query_new(world, "Position, [out] Velocity()");
ecs_entity_t e = ecs_new(world, Position);
ecs_add(world, e, Velocity);
ecs_iter_t it = ecs_query_iter(world, q);
test_assert(ecs_query_next(&it) == true);
test_assert(ecs_field_is_readonly(&it, 1) == false);
test_assert(ecs_field_is_readonly(&it, 2) == false);
test_assert(ecs_query_next(&it) == false);
ecs_fini(world);
}
void SystemMisc_rw_out_explicit_from_entity(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ECS_ENTITY(world, f, Velocity);
ecs_query_t *q = ecs_query_new(world, "Position, [out] Velocity(f)");
ecs_entity_t e = ecs_new(world, Position);
ecs_add(world, e, Velocity);
ecs_iter_t it = ecs_query_iter(world, q);
test_assert(ecs_query_next(&it) == true);
test_assert(ecs_field_is_readonly(&it, 1) == false);
test_assert(ecs_field_is_readonly(&it, 2) == false);
test_assert(ecs_query_next(&it) == false);
ecs_fini(world);
}
void SystemMisc_activate_system_for_table_w_n_pairs(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, Pair);
ECS_SYSTEM(world, Dummy, EcsOnUpdate, (Pair, *));
ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
Probe ctx = {0};
ecs_set_ctx(world, &ctx, NULL);
ECS_ENTITY(world, e, (Pair, TagA), (Pair, TagB));
test_assert(e != 0);
ecs_progress(world, 0);
test_int(ctx.count, 2);
test_int(ctx.invoked, 2);
test_int(ctx.e[0], e);
test_int(ctx.e[1], e);
test_int(ctx.c[0][0], ecs_pair(Pair, TagA));
test_int(ctx.s[0][0], 0);
test_int(ctx.c[1][0], ecs_pair(Pair, TagB));
test_int(ctx.s[1][0], 0);
ecs_fini(world);
}
void SystemMisc_get_query(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_SYSTEM(world, Dummy, EcsOnUpdate, Position);
ecs_set(world, 0, Position, {0, 0});
ecs_set(world, 0, Position, {1, 0});
ecs_set(world, 0, Position, {2, 0});
ecs_query_t *q = ecs_system_get_query(world, Dummy);
test_assert(q != NULL);
int32_t count = 0;
ecs_iter_t it = ecs_query_iter(world, q);
while (ecs_query_next(&it)) {
Position *p = ecs_field(&it, Position, 1);
test_assert(p != NULL);
int i;
for (i = 0; i < it.count; i ++) {
test_int(p[i].x, i);
count ++;
}
}
test_int(count, 3);
ecs_fini(world);
}
void SystemMisc_set_get_context(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, Tag);
int32_t ctx_a, ctx_b;
ecs_entity_t s = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, {.name = "MySystem"}),
.query.filter.terms = {{Tag}},
.callback = Dummy,
.ctx = &ctx_a
});
test_assert(s != 0);
test_assert(ecs_system_get_ctx(world, s) == &ctx_a);
test_assert(ecs_system_init(world, &(ecs_system_desc_t){
.entity = s,
.ctx = &ctx_b
}) == s);
test_assert(ecs_system_get_ctx(world, s) == &ctx_b);
ecs_fini(world);
}
void SystemMisc_set_get_binding_context(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, Tag);
int32_t ctx_a, ctx_b;
ecs_entity_t s = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, {.name = "MySystem"}),
.query.filter.terms = {{Tag}},
.callback = Dummy,
.binding_ctx = &ctx_a
});
test_assert(s != 0);
test_assert(ecs_system_get_binding_ctx(world, s) == &ctx_a);
test_assert(ecs_system_init(world, &(ecs_system_desc_t){
.entity = s,
.binding_ctx = &ctx_b
}) == s);
test_assert(ecs_system_get_binding_ctx(world, s) == &ctx_b);
ecs_fini(world);
}
void SystemMisc_deactivate_after_disable(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, Tag);
ECS_SYSTEM(world, Dummy, EcsOnUpdate, Tag);
ecs_entity_t e = ecs_new_w_id(world, Tag);
ecs_run_aperiodic(world, 0);
test_assert(!ecs_has_id(world, Dummy, EcsEmpty));
ecs_enable(world, Dummy, false);
test_assert(!ecs_has_id(world, Dummy, EcsEmpty));
test_assert(ecs_has_id(world, Dummy, EcsDisabled));
ecs_delete(world, e);
ecs_run_aperiodic(world, 0);
test_assert(!ecs_has_id(world, Dummy, EcsEmpty));
test_assert(ecs_has_id(world, Dummy, EcsDisabled));
ecs_fini(world);
}
void SystemMisc_delete_system(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, Tag);
Probe ctx = {0};
ecs_entity_t system = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, {.name = "Foo"}),
.query.filter.terms = {{.id = Tag}},
.callback = Dummy
});
test_assert(system != 0);
ecs_set_ctx(world, &ctx, NULL);
ecs_entity_t e = ecs_new_id(world);
ecs_add_id(world, e, Tag);
ecs_run(world, system, 0, NULL);
test_int(ctx.count, 1);
test_assert(ctx.system == system);
ecs_delete(world, system);
test_assert(!ecs_is_alive(world, system));
ecs_fini(world);
}
void SystemMisc_delete_pipeline_system(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, Tag);
Probe ctx = {0};
// Create system before
test_assert(ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, {.add = {ecs_dependson(EcsOnUpdate)}}),
.query.filter.terms = {{.id = Tag}},
.callback = Dummy
}) != 0);
ecs_entity_t system = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, {.add = {ecs_dependson(EcsOnUpdate)}}),
.query.filter.terms = {{.id = Tag}},
.callback = Dummy
});
test_assert(system != 0);
// Create system after
test_assert(ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, {.add = {ecs_dependson(EcsOnUpdate)}}),
.query.filter.terms = {{.id = Tag}},
.callback = Dummy
}) != 0);
ecs_set_ctx(world, &ctx, NULL);
ecs_entity_t e = ecs_new_id(world);
ecs_add_id(world, e, Tag);
ecs_progress(world, 0);
test_int(ctx.count, 3);
ctx.count = 0;
ecs_delete(world, system);
ecs_progress(world, 0);
test_int(ctx.count, 2);
ecs_fini(world);
}
static int ctx_value;
static
void ctx_free(void *ctx) {
test_assert(&ctx_value == ctx);
ctx_value ++;
}
static int binding_ctx_value;
static
void binding_ctx_free(void *ctx) {
test_assert(&binding_ctx_value == ctx);
binding_ctx_value ++;
}
static int ctx_value_2;
static
void ctx_free_2(void *ctx) {
test_assert(&ctx_value_2 == ctx);
ctx_value_2 ++;
}
static int binding_ctx_value_2;
static
void binding_ctx_free_2(void *ctx) {
test_assert(&binding_ctx_value_2 == ctx);
binding_ctx_value_2 ++;
}
void SystemMisc_delete_system_w_ctx(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, Tag);
Probe ctx = {0};
ecs_entity_t system = ecs_system_init(world, &(ecs_system_desc_t){
.query.filter.terms = {{.id = Tag}},
.callback = Dummy,
.ctx = &ctx_value,
.ctx_free = ctx_free,
.binding_ctx = &binding_ctx_value,
.binding_ctx_free = binding_ctx_free
});
test_assert(system != 0);
test_assert(ecs_system_get_ctx(world, system) == &ctx_value);
test_assert(ecs_system_get_binding_ctx(world, system)
== &binding_ctx_value);
ecs_set_ctx(world, &ctx, NULL);
ecs_entity_t e = ecs_new_id(world);
ecs_add_id(world, e, Tag);
ecs_run(world, system, 0, NULL);
test_int(ctx.count, 1);
test_assert(ctx.system == system);
ecs_delete(world, system);
test_assert(!ecs_is_alive(world, system));
ecs_fini(world);
}
void SystemMisc_update_ctx(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, Tag);
ecs_entity_t system = ecs_system_init(world, &(ecs_system_desc_t){
.query.filter.terms = {{.id = Tag}},
.callback = Dummy,
.ctx = &ctx_value,
.ctx_free = ctx_free,
.binding_ctx = &binding_ctx_value,
.binding_ctx_free = binding_ctx_free
});
test_assert(system != 0);
test_assert(ecs_system_get_ctx(world, system) == &ctx_value);
test_assert(ecs_system_get_binding_ctx(world, system)
== &binding_ctx_value);
ecs_system(world, {
.entity = system,
.ctx = &ctx_value,
.ctx_free = ctx_free,
.binding_ctx = &binding_ctx_value,
.binding_ctx_free = binding_ctx_free
});
test_int(ctx_value, 0);
test_int(binding_ctx_value, 0);
test_int(ctx_value_2, 0);
test_int(binding_ctx_value_2, 0);
ecs_system(world, {
.entity = system,
.ctx = &ctx_value_2,
.ctx_free = ctx_free_2,
.binding_ctx = &binding_ctx_value_2,
.binding_ctx_free = binding_ctx_free_2
});
test_int(ctx_value, 1);
test_int(binding_ctx_value, 1);
test_int(ctx_value_2, 0);
test_int(binding_ctx_value_2, 0);
ecs_delete(world, system);
test_int(ctx_value, 1);
test_int(binding_ctx_value, 1);
test_int(ctx_value_2, 1);
test_int(binding_ctx_value_2, 1);
ecs_fini(world);
}
static int run_invoked = 0;
static void Run(ecs_iter_t *it) {
while (ecs_iter_next(it)) {
probe_iter(it);
}
run_invoked ++;
}
static int run_2_invoked = 0;
static void Run2(ecs_iter_t *it) {
run_2_invoked ++;
ecs_iter_fini(it);
}
static void Run_call_callback(ecs_iter_t *it) {
run_invoked ++;
test_assert(it != NULL);
test_assert(it->next != NULL);
test_assert(it->callback != NULL);
while (ecs_iter_next(it)) {
it->callback(it);
}
}
void SystemMisc_run_custom_run_action(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
Probe ctx = {0};
ecs_entity_t system = ecs_system_init(world, &(ecs_system_desc_t){
.query.filter.terms = {{ .id = TagA }},
.run = Run,
.callback = Dummy,
.ctx = &ctx,
});
test_assert(system != 0);
ecs_entity_t e1 = ecs_new(world, TagA);
ecs_entity_t e2 = ecs_new(world, TagA);
ecs_entity_t e3 = ecs_new(world, TagA);
ecs_add(world, e3, TagB); // 2 tables
ecs_run(world, system, 0, NULL);
test_bool(dummy_invoked, false);
test_int(run_invoked, 1);
test_int(ctx.invoked, 2);
test_int(ctx.count, 3);
test_int(ctx.e[0], e1);
test_int(ctx.e[1], e2);
test_int(ctx.e[2], e3);
ecs_fini(world);
}
void SystemMisc_run_w_offset_limit_custom_run_action(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
Probe ctx = {0};
ecs_entity_t system = ecs_system_init(world, &(ecs_system_desc_t){
.query.filter.terms = {{ .id = TagA }},
.run = Run,
.callback = Dummy,
.ctx = &ctx,
});
test_assert(system != 0);
ecs_new(world, TagA);
ecs_entity_t e2 = ecs_new(world, TagA);
ecs_entity_t e3 = ecs_new(world, TagA);
ecs_add(world, e3, TagB); // 2 tables
ecs_run_w_filter(world, system, 0, 1, 1, NULL);
test_bool(dummy_invoked, false);
test_int(run_invoked, 1);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.e[0], e2);
ecs_fini(world);
}
void SystemMisc_pipeline_custom_run_action(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
Probe ctx = {0};
ecs_entity_t system = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, {.add = {ecs_dependson(EcsOnUpdate)}}),
.query.filter.terms = {{ .id = TagA }},
.run = Run,
.callback = Dummy,
.ctx = &ctx
});
test_assert(system != 0);
ecs_entity_t e1 = ecs_new(world, TagA);
ecs_entity_t e2 = ecs_new(world, TagA);
ecs_entity_t e3 = ecs_new(world, TagA);
ecs_add(world, e3, TagB); // 2 tables
ecs_progress(world, 0);
test_bool(dummy_invoked, false);
test_int(run_invoked, 1);
test_int(ctx.invoked, 2);
test_int(ctx.count, 3);
test_int(ctx.e[0], e1);
test_int(ctx.e[1], e2);
test_int(ctx.e[2], e3);
ecs_fini(world);
}
void SystemMisc_change_custom_run_action(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
Probe ctx = {0};
ecs_entity_t system = ecs_system_init(world, &(ecs_system_desc_t){
.query.filter.terms = {{ .id = TagA }},
.run = Run,
.callback = Dummy,
.ctx = &ctx,
});
test_assert(system != 0);
ecs_new(world, TagA);
ecs_run(world, system, 0, NULL);
test_bool(dummy_invoked, false);
test_int(run_invoked, 1);
test_int(run_2_invoked, 0);
ecs_system_init(world, &(ecs_system_desc_t){
.entity = system,
.run = Run2
});
ecs_run(world, system, 0, NULL);
test_bool(dummy_invoked, false);
test_int(run_invoked, 1);
test_int(run_2_invoked, 1);
ecs_fini(world);
}
void SystemMisc_custom_run_action_call_next(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
Probe ctx = {0};
ecs_entity_t system = ecs_system_init(world, &(ecs_system_desc_t){
.query.filter.terms = {{ .id = TagA }},
.run = Run_call_callback,
.callback = Dummy,
.ctx = &ctx,
.entity = ecs_entity(world, {.add = {ecs_dependson(EcsOnUpdate)}})
});
test_assert(system != 0);
ecs_entity_t e1 = ecs_new(world, TagA);
ecs_entity_t e2 = ecs_new(world, TagA);
ecs_entity_t e3 = ecs_new(world, TagA);
ecs_add(world, e3, TagB); // 2 tables
ecs_progress(world, 0);
test_bool(dummy_invoked, true);
test_int(run_invoked, 1);
test_int(ctx.invoked, 2);
test_int(ctx.count, 3);
test_int(ctx.e[0], e1);
test_int(ctx.e[1], e2);
test_int(ctx.e[2], e3);
ecs_fini(world);
}
void SystemMisc_system_w_short_notation(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, Tag);
Probe ctx = {0};
ecs_entity_t system = ecs_system(world, {
.query.filter.terms = {{ .id = Tag }},
.callback = Dummy,
.ctx = &ctx,
.entity = ecs_entity(world, {.add = {ecs_dependson(EcsOnUpdate)}})
});
test_assert(system != 0);
ecs_entity_t e = ecs_new(world, Tag);
ecs_progress(world, 0);
test_bool(dummy_invoked, true);
test_int(ctx.invoked, 1);
test_int(ctx.count, 1);
test_int(ctx.e[0], e);
ecs_fini(world);
}
void SystemMisc_update_interval_w_system_init(void) {
ecs_world_t *world = ecs_init();
ecs_entity_t system = ecs_system(world, {
.callback = Dummy,
});
test_assert(system != 0);
test_int(ecs_get_interval(world, system), 0);
ecs_system(world, {
.entity = system,
.interval = 1.0
});
test_int(ecs_get_interval(world, system), 1.0);
ecs_fini(world);
}
void SystemMisc_update_rate_w_system_init(void) {
ecs_world_t *world = ecs_init();
ecs_entity_t system = ecs_system(world, {
.callback = Dummy,
});
test_assert(system != 0);
test_assert(ecs_get(world, system, EcsRateFilter) == NULL);
ecs_system(world, {
.entity = system,
.rate = 2.0
});
const EcsRateFilter *r = ecs_get(world, system, EcsRateFilter);
test_assert(r != NULL);
test_int(r->rate, 2.0);
ecs_fini(world);
}
void SystemMisc_system_w_interval_rate_stop_timer(void) {
ecs_world_t *world = ecs_init();
ecs_entity_t system = ecs_system(world, {
.entity = ecs_entity(world, { .add = {ecs_dependson(EcsOnUpdate)} }),
.interval = 1.0,
.rate = 3.0,
.callback = Dummy
});
for (int i = 0; i < 5; i ++) {
ecs_progress(world, 0.5);
test_assert(dummy_invoked == 0);
}
ecs_progress(world, 0.5);
test_assert(dummy_invoked == 1);
dummy_invoked = 0;
ecs_stop_timer(world, system);
for (int i = 0; i < 5; i ++) {
ecs_progress(world, 0.5);
test_assert(dummy_invoked == 0);
}
ecs_progress(world, 0.5);
test_assert(dummy_invoked == 0);
ecs_fini(world);
}
static
int32_t sys_a_invoked = false;
static
void SA(ecs_iter_t *it) {
sys_a_invoked = true;
}
static
int32_t sys_b_invoked = false;
static
void SB(ecs_iter_t *it) {
sys_b_invoked = true;
}
static
int32_t sys_c_invoked = false;
static
void SC(ecs_iter_t *it) {
sys_c_invoked = true;
}
void SystemMisc_system_same_interval_same_tick(void) {
ecs_world_t *world = ecs_init();
ecs_system(world, {
.entity = ecs_entity(world, { .add = {ecs_dependson(EcsOnUpdate)} }),
.interval = 1.0,
.callback = SA
});
ecs_system(world, {
.entity = ecs_entity(world, { .add = {ecs_dependson(EcsOnUpdate)} }),
.interval = 1.0,
.callback = SB
});
ecs_system(world, {
.entity = ecs_entity(world, { .add = {ecs_dependson(EcsOnUpdate)} }),
.interval = 1.0,
.callback = SC
});
while (!sys_a_invoked) {
test_assert(!sys_b_invoked);
test_assert(!sys_c_invoked);
ecs_progress(world, 0.0001);
}
test_assert(sys_b_invoked);
test_assert(sys_c_invoked);
ecs_fini(world);
}