#include #include void World_setup(void) { ecs_log_set_level(-3); } static void Move(ecs_iter_t *it) { Position *pos = ecs_field(it, Position, 1); Velocity *vel = ecs_field(it, Velocity, 2); probe_iter(it); int row; for (row = 0; row < it->count; row ++) { Position *p = &pos[row]; Velocity *v = &vel[row]; p->x += v->x * it->delta_time; p->y += v->y * it->delta_time; } } void World_progress_w_0(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_ENTITY(world, e1, Position, Velocity); ECS_SYSTEM(world, Move, EcsOnUpdate, Position, Velocity); Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); ecs_set(world, e1, Position, {0, 0}); ecs_set(world, e1, Velocity, {1, 2}); ecs_progress(world, 0); test_int(ctx.count, 1); test_int(ctx.invoked, 1); test_int(ctx.system, Move); test_int(ctx.term_count, 2); test_null(ctx.param); test_int(ctx.e[0], e1); test_int(ctx.c[0][0], ecs_id(Position)); test_int(ctx.c[0][1], ecs_id(Velocity)); test_int(ctx.s[0][0], 0); test_int(ctx.s[0][1], 0); const Position *p = ecs_get(world, e1, Position); test_assert(p != NULL); test_assert(p->x != 0); test_assert(p->y != 0); ecs_fini(world); } void World_progress_w_t(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_ENTITY(world, e1, Position, Velocity); ECS_SYSTEM(world, Move, EcsOnUpdate, Position, Velocity); Probe ctx = {0}; ecs_set_ctx(world, &ctx, NULL); ecs_set(world, e1, Position, {0, 0}); ecs_set(world, e1, Velocity, {1, 2}); ecs_progress(world, 2); test_int(ctx.count, 1); test_int(ctx.invoked, 1); test_int(ctx.system, Move); test_int(ctx.term_count, 2); test_null(ctx.param); test_int(ctx.e[0], e1); test_int(ctx.c[0][0], ecs_id(Position)); test_int(ctx.c[0][1], ecs_id(Velocity)); test_int(ctx.s[0][0], 0); test_int(ctx.s[0][1], 0); const Position *p = ecs_get(world, e1, Position); test_assert(p != NULL); test_int(p->x, 2); test_int(p->y, 4); ecs_fini(world); } void World_entity_range_offset(void) { ecs_world_t *world = ecs_init(); ecs_set_entity_range(world, 5000, 0); ecs_entity_t e = ecs_new(world, 0); test_int(e, 5000); ecs_fini(world); } void World_entity_range_offset_out_of_range(void) { install_test_abort(); ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); ecs_enable_range_check(world, true); ecs_set_entity_range(world, 2000, 0); test_expect_abort(); ecs_add(world, 1500, Position); ecs_fini(world); } void World_entity_range_limit_out_of_range(void) { install_test_abort(); ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); ecs_enable_range_check(world, true); ecs_set_entity_range(world, 0, 2000); test_expect_abort(); ecs_add(world, 2500, Position); ecs_fini(world); } void World_entity_range_out_of_range_check_disabled(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); ecs_ensure(world, 4999); ecs_enable_range_check(world, false); ecs_set_entity_range(world, 5000, 10000); /* Validate that range is being used when issuing new ids */ ecs_entity_t e = ecs_new(world, 0); test_int(e, 5000); /* Validate that application does not abort when changing out of range */ ecs_entity_t e2 = ecs_set(world, 4999, Position, {10, 20}); test_int(e2, 4999); test_assert( ecs_has(world, e2, Position)); const Position *p = ecs_get(world, e2, Position); test_assert(p != NULL); test_int(p->x, 10); test_int(p->y, 20); ecs_fini(world); } void World_entity_range_check_after_delete(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); ecs_enable_range_check(world, true); ecs_set_entity_range(world, 5000, 10000); ecs_entity_t e = ecs_new(world, 0); test_assert(e != 0); test_assert(e == 5000); ecs_delete(world, e); e = ecs_new(world, 0); test_assert(e != 0); test_assert((uint32_t)e == 5000); ecs_fini(world); } void World_entity_range_add_existing_staged(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t e = ecs_new(world, Position); test_assert(e != 0); test_assert(e < 1000); ecs_set_entity_range(world, 1000, 1500); ecs_readonly_begin(world); ecs_world_t *stage = ecs_get_stage(world, 0); ecs_add(stage, e, Velocity); ecs_readonly_end(world); ecs_fini(world); } void World_entity_range_add_in_range_staged(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_set_entity_range(world, 500, 1000); ecs_entity_t e = ecs_new(world, Position); test_assert(e == 500); ecs_readonly_begin(world); ecs_world_t *stage = ecs_get_stage(world, 0); ecs_add(stage, e, Velocity); ecs_readonly_end(world); ecs_fini(world); } void AddOutOfRange(ecs_iter_t *it) { ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 2); int i; for (i = 0; i < it->count; i ++) { test_expect_abort(); ecs_add(it->world, 1001, Velocity); } } void World_entity_range_add_out_of_range_staged(void) { install_test_abort(); ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_enable_range_check(world, true); ecs_set_entity_range(world, 500, 1000); /* Dummy entity to invoke the system */ ecs_entity_t e = ecs_new(world, Position); test_assert(e == 500); ecs_readonly_begin(world); ecs_world_t *stage = ecs_get_stage(world, 0); ecs_add(stage, e, Velocity); ecs_readonly_end(world); ecs_fini(world); } void World_get_tick(void) { ecs_world_t *world = ecs_init(); const ecs_world_info_t *stats = ecs_get_world_info(world); test_int(stats->frame_count_total, 0); ecs_progress(world, 1); test_int(stats->frame_count_total, 1); ecs_progress(world, 1); test_int(stats->frame_count_total, 2); ecs_fini(world); } static int32_t malloc_count; static void *test_malloc(ecs_size_t size) { malloc_count ++; return malloc(size); } static void *test_calloc(ecs_size_t size) { malloc_count ++; return calloc(size, 1); } static void *test_realloc(void *old_ptr, ecs_size_t size) { malloc_count ++; return realloc(old_ptr, size); } void World_dim(void) { ecs_os_set_api_defaults(); ecs_os_api_t os_api = ecs_os_api; os_api.malloc_ = test_malloc; os_api.calloc_ = test_calloc; os_api.realloc_ = test_realloc; ecs_os_set_api(&os_api); ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); /* Create single entity so that the table exists. This makes the allocation * counts more predictable, as new_w_count won't trigger table creation */ ecs_new(world, Position); ecs_dim(world, 1100); malloc_count = 0; ecs_bulk_new(world, Position, 500); test_int(malloc_count, 2); malloc_count = 0; ecs_bulk_new(world, Position, 500); test_int(malloc_count, 2); ecs_fini(world); } static void TOnLoad(ecs_iter_t *it) { Position *p = ecs_field(it, Position, 1); int i; for (i = 0; i < it->count; i ++) { test_int(p[i].x, 0); p[i].x ++; } } static void TPostLoad(ecs_iter_t *it) { Position *p = ecs_field(it, Position, 1); int i; for (i = 0; i < it->count; i ++) { test_int(p[i].x, 1); p[i].x ++; } } static void TPreUpdate(ecs_iter_t *it) { Position *p = ecs_field(it, Position, 1); int i; for (i = 0; i < it->count; i ++) { test_int(p[i].x, 2); p[i].x ++; } } static void TOnUpdate(ecs_iter_t *it) { Position *p = ecs_field(it, Position, 1); int i; for (i = 0; i < it->count; i ++) { test_int(p[i].x, 3); p[i].x ++; } } static void TOnValidate(ecs_iter_t *it) { Position *p = ecs_field(it, Position, 1); int i; for (i = 0; i < it->count; i ++) { test_int(p[i].x, 4); p[i].x ++; } } static void TPostUpdate(ecs_iter_t *it) { Position *p = ecs_field(it, Position, 1); int i; for (i = 0; i < it->count; i ++) { test_int(p[i].x, 5); p[i].x ++; } } static void TPreStore(ecs_iter_t *it) { Position *p = ecs_field(it, Position, 1); int i; for (i = 0; i < it->count; i ++) { test_int(p[i].x, 6); p[i].x ++; } } static void TOnStore(ecs_iter_t *it) { Position *p = ecs_field(it, Position, 1); int i; for (i = 0; i < it->count; i ++) { test_int(p[i].x, 7); p[i].x ++; } } static void TManual(ecs_iter_t *it) { Position *p = ecs_field(it, Position, 1); int i; for (i = 0; i < it->count; i ++) { test_int(p[i].x, 8); p[i].x ++; } } void World_phases(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); ECS_SYSTEM(world, TOnLoad, EcsOnLoad, Position); ECS_SYSTEM(world, TPostLoad, EcsPostLoad, Position); ECS_SYSTEM(world, TPreUpdate, EcsPreUpdate, Position); ECS_SYSTEM(world, TOnUpdate, EcsOnUpdate, Position); ECS_SYSTEM(world, TOnValidate, EcsOnValidate, Position); ECS_SYSTEM(world, TPostUpdate, EcsPostUpdate, Position); ECS_SYSTEM(world, TPreStore, EcsPreStore, Position); ECS_SYSTEM(world, TOnStore, EcsOnStore, Position); ECS_SYSTEM(world, TManual, 0, Position); ecs_entity_t e = ecs_new(world, Position); test_assert(e != 0); ecs_set(world, e, Position, {0, 0}); ecs_progress(world, 1); const Position *p = ecs_get(world, e, Position); test_int(p->x, 8); ecs_run(world, TManual, 0, NULL); test_int(p->x, 9); ecs_fini(world); } void World_phases_match_in_create(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); ecs_entity_t e = ecs_new(world, Position); test_assert(e != 0); ecs_set(world, e, Position, {0, 0}); ECS_SYSTEM(world, TOnLoad, EcsOnLoad, Position); ECS_SYSTEM(world, TPostLoad, EcsPostLoad, Position); ECS_SYSTEM(world, TPreUpdate, EcsPreUpdate, Position); ECS_SYSTEM(world, TOnUpdate, EcsOnUpdate, Position); ECS_SYSTEM(world, TOnValidate, EcsOnValidate, Position); ECS_SYSTEM(world, TPostUpdate, EcsPostUpdate, Position); ECS_SYSTEM(world, TPreStore, EcsPreStore, Position); ECS_SYSTEM(world, TOnStore, EcsOnStore, Position); ECS_SYSTEM(world, TManual, 0, Position); ecs_progress(world, 1); const Position *p = ecs_get(world, e, Position); test_int(p->x, 8); ecs_run(world, TManual, 0, NULL); test_int(p->x, 9); ecs_fini(world); } static void TMergeOnLoad(ecs_iter_t *it) { Position *p = ecs_field(it, Position, 1); ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); int i; for (i = 0; i < it->count; i ++) { test_int(p[i].x, 0); ecs_set(it->world, it->entities[i], Position, {p[i].x + 1, 0}); } } static void TMergePostLoad(ecs_iter_t *it) { Position *p = ecs_field(it, Position, 1); ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); int i; for (i = 0; i < it->count; i ++) { test_int(p[i].x, 1); ecs_set(it->world, it->entities[i], Position, {p[i].x + 1, 0}); } } static void TMergePreUpdate(ecs_iter_t *it) { Position *p = ecs_field(it, Position, 1); ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); int i; for (i = 0; i < it->count; i ++) { test_int(p[i].x, 2); ecs_set(it->world, it->entities[i], Position, {p[i].x + 1, 0}); } } static void TMergeOnUpdate(ecs_iter_t *it) { Position *p = ecs_field(it, Position, 1); ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); int i; for (i = 0; i < it->count; i ++) { test_int(p[i].x, 3); ecs_set(it->world, it->entities[i], Position, {p[i].x + 1, 0}); } } static void TMergeOnValidate(ecs_iter_t *it) { Position *p = ecs_field(it, Position, 1); ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); int i; for (i = 0; i < it->count; i ++) { test_int(p[i].x, 4); ecs_set(it->world, it->entities[i], Position, {p[i].x + 1, 0}); } } static void TMergePostUpdate(ecs_iter_t *it) { Position *p = ecs_field(it, Position, 1); ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); int i; for (i = 0; i < it->count; i ++) { test_int(p[i].x, 5); ecs_set(it->world, it->entities[i], Position, {p[i].x + 1, 0}); } } static void TMergePreStore(ecs_iter_t *it) { Position *p = ecs_field(it, Position, 1); ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); int i; for (i = 0; i < it->count; i ++) { test_int(p[i].x, 6); ecs_set(it->world, it->entities[i], Position, {p[i].x + 1, 0}); } } static void TMergeOnStore(ecs_iter_t *it) { Position *p = ecs_field(it, Position, 1); ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); int i; for (i = 0; i < it->count; i ++) { test_int(p[i].x, 7); ecs_set(it->world, it->entities[i], Position, {p[i].x + 1, 0}); } } static void TMergeManual(ecs_iter_t *it) { Position *p = ecs_field(it, Position, 1); ecs_id_t ecs_id(Position) = ecs_field_id(it, 1); int i; for (i = 0; i < it->count; i ++) { test_int(p[i].x, 8); ecs_set(it->world, it->entities[i], Position, {p[i].x + 1, 0}); } } void World_phases_w_merging(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); ECS_SYSTEM(world, TMergeOnLoad, EcsOnLoad, Position, [out] Position()); ECS_SYSTEM(world, TMergePostLoad, EcsPostLoad, Position, [out] Position()); ECS_SYSTEM(world, TMergePreUpdate, EcsPreUpdate, Position, [out] Position()); ECS_SYSTEM(world, TMergeOnUpdate, EcsOnUpdate, Position, [out] Position()); ECS_SYSTEM(world, TMergeOnValidate, EcsOnValidate, Position, [out] Position()); ECS_SYSTEM(world, TMergePostUpdate, EcsPostUpdate, Position, [out] Position()); ECS_SYSTEM(world, TMergePreStore, EcsPreStore, Position, [out] Position()); ECS_SYSTEM(world, TMergeOnStore, EcsOnStore, Position, [out] Position()); ECS_SYSTEM(world, TMergeManual, 0, Position); ecs_entity_t e = ecs_new(world, Position); test_assert(e != 0); ecs_set(world, e, Position, {0, 0}); ecs_progress(world, 1); const Position *p = ecs_get(world, e, Position); test_int(p->x, 8); ecs_run(world, TMergeManual, 0, NULL); p = ecs_get(world, e, Position); test_int(p->x, 9); ecs_fini(world); } static void TimeCheck(ecs_iter_t *it) { test_assert(it->delta_time > 0); } void World_measure_time(void) { ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); ECS_SYSTEM(world, TimeCheck, EcsOnLoad, Position); ecs_entity_t e = ecs_new(world, Position); test_assert(e != 0); int i = 0; for (i = 0; i < 1000; i ++) { ecs_progress(world, 0); } ecs_fini(world); } void World_control_fps(void) { test_is_flaky(); ecs_world_t *world = ecs_init(); ECS_COMPONENT(world, Position); ECS_SYSTEM(world, TimeCheck, EcsOnLoad, Position); ecs_entity_t e = ecs_new(world, Position); test_assert(e != 0); double start, now = 0; ecs_set_target_fps(world, 20); /* Run a few times to give the code an opportunity to calibrate */ ecs_progress(world, 0); ecs_progress(world, 0); ecs_progress(world, 0); ecs_progress(world, 0); ecs_progress(world, 0); const ecs_world_info_t *stats = ecs_get_world_info(world); /* Run for one second */ int count = 0; do { ecs_progress(world, 0); if (!count) { start = stats->delta_time; } now += stats->delta_time; count ++; } while ((now - start) < 1.0); /* CI can be unpredictable, just make sure it's in the right ballpark */ test_assert(count >= 15); test_assert(count < 25); ecs_fini(world); } static void busy_wait(float wait_time) { ecs_time_t start, t; ecs_os_get_time(&start); do { t = start; } while (ecs_time_measure(&t) < wait_time); } static void BusySystem(ecs_iter_t *it) { /* Spend 14msec doing something */ busy_wait(0.014); } void World_control_fps_busy_system(void) { test_is_flaky(); ecs_world_t *world = ecs_init(); ECS_SYSTEM(world, BusySystem, EcsOnUpdate, 0); double start, now = 0; ecs_set_target_fps(world, 20); const ecs_world_info_t *stats = ecs_get_world_info(world); /* Run for one second */ int count = 0; do { ecs_progress(world, 0); if (!count) { start = stats->delta_time; } now += stats->delta_time; count ++; } while ((now - start) < 1.0); /* FPS control relies on sleep, which relies on the OS scheduler. Therefore * pick a wide enough range to avoid tests failing at random. */ test_assert(count >= 15); test_assert(count < 25); ecs_fini(world); } void World_control_fps_busy_app(void) { test_is_flaky(); ecs_world_t *world = ecs_init(); double start, now = 0; ecs_set_target_fps(world, 20); const ecs_world_info_t *stats = ecs_get_world_info(world); /* Run for one second */ int count = 0; do { ecs_progress(world, 0); if (!count) { start = stats->delta_time; } now += stats->delta_time; count ++; busy_wait(0.014); } while ((now - start) < 1.0); /* FPS control relies on sleep, which relies on the OS scheduler. Therefore * pick a wide enough range to avoid tests failing at random. */ test_assert(count >= 15); test_assert(count < 25); ecs_fini(world); } void World_measure_fps_vs_actual(void) { test_is_flaky(); ecs_world_t *world = ecs_init(); ecs_set_target_fps(world, 60); /* Run 10 times, test if one second has passed */ ecs_time_t t; ecs_os_get_time(&t); int32_t i; for (i = 0; i < 60; i ++) { ecs_progress(world, 0); } float elapsed = ecs_time_measure(&t); test_assert(elapsed >= 0.9); ecs_fini(world); } void World_measure_delta_time_vs_actual(void) { test_is_flaky(); ecs_world_t *world = ecs_init(); ecs_set_target_fps(world, 60); const ecs_world_info_t *stats = ecs_get_world_info(world); /* Run 10 times, test if one second has passed */ ecs_time_t t; float delta_time = 0; ecs_os_get_time(&t); int32_t i; for (i = 0; i < 60; i ++) { ecs_progress(world, 0); delta_time += stats->delta_time; } float elapsed = ecs_time_measure(&t); test_assert(delta_time - elapsed < 0.1); test_assert(elapsed >= 0.9); ecs_fini(world); } static void RandomSystem(ecs_iter_t *it) { /* wait at most 16msec */ float rnd_time = ((float)rand() / (float)RAND_MAX) * 0.016; busy_wait(rnd_time); } void World_control_fps_random_system(void) { test_is_flaky(); ecs_world_t *world = ecs_init(); ECS_SYSTEM(world, RandomSystem, EcsOnUpdate, 0); double start, now = 0; ecs_set_target_fps(world, 20); const ecs_world_info_t *stats = ecs_get_world_info(world); /* Run for one second */ int count = 0; do { ecs_progress(world, 0); if (!count) { start = stats->delta_time; } now += stats->delta_time; count ++; } while ((now - start) < 1.0); /* FPS control relies on sleep, which relies on the OS scheduler. Therefore * pick a wide enough range to avoid tests failing at random. */ test_assert(count >= 15); test_assert(count < 25); ecs_fini(world); } void World_control_fps_random_app(void) { test_is_flaky(); ecs_world_t *world = ecs_init(); double start, now = 0; ecs_set_target_fps(world, 20); const ecs_world_info_t *stats = ecs_get_world_info(world); /* Run for one second */ int count = 0; do { ecs_progress(world, 0); if (!count) { start = stats->delta_time; } now += stats->delta_time; count ++; float rnd_time = ((float)rand() / (float)RAND_MAX) * 0.016; busy_wait(rnd_time); } while ((now - start) < 1.0); /* FPS control relies on sleep, which relies on the OS scheduler. Therefore * pick a wide enough range to avoid tests failing at random. */ test_assert(count >= 15); test_assert(count < 25); ecs_fini(world); } void World_quit(void) { ecs_world_t *world = ecs_init(); int32_t count = 0; while (ecs_progress(world, 0)) { test_int(count, 0); ecs_quit(world); count ++; } test_int(count, 1); ecs_fini(world); } void World_get_delta_time(void) { ecs_world_t *world = ecs_init(); const ecs_world_info_t *stats = ecs_get_world_info(world); test_int(stats->delta_time, 0); ecs_progress(world, 1.0); test_flt(stats->delta_time, 1.0); ecs_fini(world); } void World_get_delta_time_auto(void) { ecs_world_t *world = ecs_init(); const ecs_world_info_t *stats = ecs_get_world_info(world); test_int(stats->delta_time, 0); ecs_progress(world, 0); test_assert(stats->delta_time != 0); ecs_fini(world); } void World_recreate_world(void) { ecs_world_t *world = ecs_init(); test_assert(ecs_fini(world) == 0); world = ecs_init(); test_assert(ecs_fini(world) == 0); } void World_recreate_world_w_component(void) { ecs_world_t *world = ecs_init(); test_assert(world != NULL); { ECS_COMPONENT(world, Position); test_assert(ecs_id(Position) != 0); } test_assert(ecs_fini(world) == 0); { world = ecs_init(); test_assert(world != NULL); ECS_COMPONENT(world, Position); test_assert(ecs_id(Position) != 0); test_assert(ecs_fini(world) == 0); } } void World_no_threading(void) { ecs_os_set_api_defaults(); ecs_os_api_t os_api = ecs_os_api; os_api.mutex_new_ = NULL; ecs_os_set_api(&os_api); ecs_world_t *world = ecs_init(); test_assert(world != NULL); ecs_fini(world); } void World_no_time(void) { ecs_os_set_api_defaults(); ecs_os_api_t os_api = ecs_os_api; os_api.get_time_ = NULL; ecs_os_set_api(&os_api); ecs_world_t *world = ecs_init(); test_assert(world != NULL); ecs_fini(world); } void World_is_entity_enabled(void) { ecs_world_t *world = ecs_init(); ecs_entity_t e = ecs_new(world, 0); test_assert( ecs_has_id(world, e, EcsDisabled) == false); ecs_fini(world); } static int zero_time_scale_invoked = 0; void ZeroTimeScale(ecs_iter_t *it) { test_assert(it->delta_time == 0.0); zero_time_scale_invoked ++; } void World_system_time_scale(void) { ecs_world_t *world = ecs_init(); ECS_TAG(world, Tag); ecs_new_w_id(world, Tag); ecs_set_time_scale(world, 0); ECS_SYSTEM(world, ZeroTimeScale, EcsOnUpdate, Tag); ecs_progress(world, 0); ecs_progress(world, 0); const ecs_world_info_t *info = ecs_get_world_info(world); test_assert(info->delta_time == 0.0); test_int(zero_time_scale_invoked, 2); ecs_fini(world); } void World_ensure_empty_root(void) { ecs_world_t *world = ecs_init(); ecs_query_t *q = ecs_query_new(world, "!(ChildOf, *)"); ecs_iter_t it = ecs_query_iter(world, q); /* Make sure that the only entity in the root is the flecs module */ test_assert(ecs_query_next(&it)); test_int(it.count, 1); test_assert(it.entities[0] == EcsFlecs); /* Entity for the query */ test_assert(ecs_query_next(&it)); test_int(it.count, 1); test_assert(ecs_has_id(world, it.entities[0], EcsQuery)); test_assert(!ecs_query_next(&it)); ecs_fini(world); } void World_register_alias_twice_same_entity(void) { ecs_world_t *world = ecs_init(); ecs_entity_t e = ecs_new_id(world); ecs_set_alias(world, e, "Foo"); ecs_set_alias(world, e, "Foo"); ecs_entity_t f = ecs_lookup(world, "Foo"); test_assert(f == e); ecs_fini(world); } void World_register_alias_twice_different_entity(void) { install_test_abort(); ecs_world_t *world = ecs_init(); ecs_entity_t e = ecs_new_id(world); ecs_entity_t f = ecs_new_id(world); ecs_set_alias(world, e, "Foo"); test_expect_abort(); ecs_set_alias(world, f, "Foo"); } void World_redefine_component(void) { ecs_world_t *world = ecs_init(); ecs_entity_t c = ecs_component_init(world, &(ecs_component_desc_t){ .entity = ecs_entity(world, { .name = "flecs.core.Component", .symbol = "EcsComponent" }), .type.size = ECS_SIZEOF(EcsComponent), .type.alignment = ECS_ALIGNOF(EcsComponent) }); test_assert(c == ecs_id(EcsComponent)); ecs_fini(world); } void World_delete_empty_tables_after_mini(void) { ecs_world_t *world = ecs_mini(); const ecs_world_info_t *info = ecs_get_world_info(world); int32_t old_empty_table_count = info->empty_table_count; int32_t deleted; deleted = ecs_delete_empty_tables(world, 0, 0, 1, 0, 0); /* Increase to 1 */ test_int(deleted, 0); deleted = ecs_delete_empty_tables(world, 0, 0, 1, 0, 0); /* Delete */ test_assert(deleted != 0); test_int(info->empty_table_count + deleted, old_empty_table_count); ecs_fini(world); } void World_delete_empty_tables_after_init(void) { ecs_world_t *world = ecs_init(); int32_t deleted; deleted = ecs_delete_empty_tables(world, 0, 0, 1, 0, 0); /* Increase to 1 */ test_int(deleted, 0); deleted = ecs_delete_empty_tables(world, 0, 0, 1, 0, 0); /* Delete */ test_assert(deleted != 0); ecs_fini(world); } void World_delete_1000_empty_tables(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, Tag); ecs_run_aperiodic(world, 0); const ecs_world_info_t *info = ecs_get_world_info(world); int32_t old_empty_table_count = info->empty_table_count; ecs_entity_t e = ecs_new(world, Tag); for (int i = 0; i < 1000; i ++) { ecs_add_id(world, e, ecs_new_id(world)); } ecs_run_aperiodic(world, 0); test_int(info->empty_table_count, old_empty_table_count + 1000); int32_t deleted; deleted = ecs_delete_empty_tables(world, 0, 0, 1, 0, 0); /* Increase to 1 */ test_int(deleted, 0); deleted = ecs_delete_empty_tables(world, 0, 0, 1, 0, 0); /* Delete */ test_assert(deleted != 0); test_assert(deleted >= 1000); test_assert(info->empty_table_count <= old_empty_table_count); ecs_fini(world); } void World_delete_empty_tables_for_id(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_TAG(world, TagB); ecs_run_aperiodic(world, 0); const ecs_world_info_t *info = ecs_get_world_info(world); int32_t old_empty_table_count = info->empty_table_count; ecs_entity_t e1 = ecs_new(world, TagA); for (int i = 0; i < 500; i ++) { ecs_add_id(world, e1, ecs_new_id(world)); } ecs_entity_t e2 = ecs_new(world, TagB); for (int i = 0; i < 500; i ++) { ecs_add_id(world, e2, ecs_new_id(world)); } ecs_run_aperiodic(world, 0); test_int(info->empty_table_count, old_empty_table_count + 1000); int32_t deleted; deleted = ecs_delete_empty_tables(world, TagA, 0, 1, 0, 0); /* Increase to 1 */ test_int(deleted, 0); deleted = ecs_delete_empty_tables(world, TagA, 0, 1, 0, 0); /* Delete */ test_assert(deleted != 0); test_assert(deleted >= 500); test_assert(deleted < 1000); test_assert((info->empty_table_count - 500) <= old_empty_table_count); ecs_fini(world); } void World_use_after_delete_empty(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_TAG(world, TagB); ecs_entity_t e = ecs_new_id(world); ecs_add(world, e, TagA); ecs_add(world, e, TagB); ecs_remove(world, e, TagA); int32_t deleted; deleted = ecs_delete_empty_tables(world, 0, 0, 1, 0, 0); test_assert(deleted == 0); deleted = ecs_delete_empty_tables(world, 0, 0, 1, 0, 0); test_assert(deleted != 0); ecs_add(world, e, TagA); test_assert( ecs_has(world, e, TagA)); test_assert( ecs_has(world, e, TagB)); ecs_fini(world); } void World_use_after_clear_empty(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_TAG(world, TagB); ecs_entity_t e = ecs_new_id(world); ecs_add(world, e, TagA); ecs_add(world, e, TagB); ecs_remove(world, e, TagA); int32_t deleted; deleted = ecs_delete_empty_tables(world, 0, 1, 0, 0, 0); test_assert(deleted == 0); deleted = ecs_delete_empty_tables(world, 0, 1, 0, 0, 0); test_assert(deleted == 0); ecs_add(world, e, TagA); test_assert( ecs_has(world, e, TagA)); test_assert( ecs_has(world, e, TagB)); ecs_fini(world); } void World_use_after_delete_empty_w_component(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t e = ecs_new_id(world); ecs_add(world, e, Position); ecs_add(world, e, Velocity); test_assert( ecs_has(world, e, Position)); ecs_remove(world, e, Velocity); test_assert( ecs_has(world, e, Position)); test_assert( !ecs_has(world, e, Velocity)); int32_t deleted; deleted = ecs_delete_empty_tables(world, 0, 0, 1, 0, 0); test_assert(deleted == 0); test_bool(true, ecs_is_alive(world, ecs_id(Position))); test_bool(true, ecs_is_alive(world, ecs_id(Velocity))); test_assert( ecs_has(world, e, Position)); deleted = ecs_delete_empty_tables(world, 0, 0, 1, 0, 0); test_assert(deleted != 0); test_bool(true, ecs_is_alive(world, ecs_id(Position))); test_bool(true, ecs_is_alive(world, ecs_id(Velocity))); test_assert( ecs_has(world, e, Position)); ecs_add(world, e, Velocity); test_assert( ecs_has(world, e, Position)); test_assert( ecs_has(world, e, Velocity)); ecs_fini(world); } void World_use_after_clear_empty_w_component(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t e = ecs_new_id(world); ecs_add(world, e, Position); ecs_add(world, e, Velocity); test_assert( ecs_has(world, e, Position)); ecs_remove(world, e, Velocity); test_assert( ecs_has(world, e, Position)); test_assert( !ecs_has(world, e, Velocity)); int32_t deleted; deleted = ecs_delete_empty_tables(world, 0, 1, 0, 0, 0); test_assert(deleted == 0); test_bool(true, ecs_is_alive(world, ecs_id(Position))); test_bool(true, ecs_is_alive(world, ecs_id(Velocity))); test_assert( ecs_has(world, e, Position)); deleted = ecs_delete_empty_tables(world, 0, 1, 0, 0, 0); test_assert(deleted == 0); test_bool(true, ecs_is_alive(world, ecs_id(Position))); test_bool(true, ecs_is_alive(world, ecs_id(Velocity))); test_assert( ecs_has(world, e, Position)); ecs_add(world, e, Velocity); test_assert( ecs_has(world, e, Position)); test_assert( ecs_has(world, e, Velocity)); ecs_fini(world); } void World_use_after_clear_empty_w_component_w_lifecycle(void) { ecs_world_t *world = ecs_mini(); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_set_hooks(world, Position, { .ctor = ecs_default_ctor }); ecs_set_hooks(world, Velocity, { .ctor = ecs_default_ctor }); ecs_entity_t e = ecs_new_id(world); ecs_add(world, e, Position); ecs_add(world, e, Velocity); test_assert( ecs_has(world, e, Position)); ecs_remove(world, e, Velocity); test_assert( ecs_has(world, e, Position)); test_assert( !ecs_has(world, e, Velocity)); int32_t deleted; deleted = ecs_delete_empty_tables(world, 0, 1, 0, 0, 0); test_assert(deleted == 0); test_bool(true, ecs_is_alive(world, ecs_id(Position))); test_bool(true, ecs_is_alive(world, ecs_id(Velocity))); test_assert( ecs_has(world, e, Position)); deleted = ecs_delete_empty_tables(world, 0, 1, 0, 0, 0); test_assert(deleted == 0); test_bool(true, ecs_is_alive(world, ecs_id(Position))); test_bool(true, ecs_is_alive(world, ecs_id(Velocity))); test_assert( ecs_has(world, e, Position)); ecs_add(world, e, Velocity); test_assert( ecs_has(world, e, Position)); test_assert( ecs_has(world, e, Velocity)); ecs_fini(world); } void World_use_after_clear_unused(void) { ecs_world_t *world = ecs_mini(); ECS_TAG(world, TagA); ECS_TAG(world, TagB); int32_t deleted; deleted = ecs_delete_empty_tables(world, 0, 1, 0, 0, 0); test_assert(deleted == 0); deleted = ecs_delete_empty_tables(world, 0, 1, 0, 0, 0); test_assert(deleted == 0); ecs_entity_t e = ecs_new_id(world); ecs_add(world, e, TagA); ecs_add(world, e, TagB); test_assert(ecs_has(world, e, TagA)); test_assert(ecs_has(world, e, TagB)); ecs_fini(world); } static ECS_COMPONENT_DECLARE(Test); typedef struct Test { uint32_t value; } Test; static int at_fini_test_invoked = 0; static void at_fini_test( ecs_world_t* world, void* context) { test_assert(ecs_singleton_get_mut(world, Test) != NULL); at_fini_test_invoked = 1; } void World_get_mut_in_at_fini(void) { ecs_world_t* world = ecs_mini(); ECS_COMPONENT_DEFINE(world, Test); ecs_singleton_set(world, Test, { 42 }); ecs_atfini(world, at_fini_test, NULL); ecs_fini(world); test_int(at_fini_test_invoked, 1); } void World_get_type_info(void) { ecs_world_t* world = ecs_mini(); ECS_COMPONENT(world, Position); const ecs_type_info_t *ti = ecs_get_type_info(world, ecs_id(Position)); test_assert(ti != NULL); test_int(ti->size, ECS_SIZEOF(Position)); test_int(ti->alignment, ECS_ALIGNOF(Position)); test_uint(ti->component, ecs_id(Position)); ecs_fini(world); } void World_get_type_info_after_delete_with(void) { ecs_world_t* world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_delete_with(world, ecs_id(Position)); const ecs_type_info_t *ti = ecs_get_type_info(world, ecs_id(Position)); test_assert(ti != NULL); test_int(ti->size, ECS_SIZEOF(Position)); test_int(ti->alignment, ECS_ALIGNOF(Position)); test_uint(ti->component, ecs_id(Position)); ecs_fini(world); } void World_get_type_info_after_reuse(void) { ecs_world_t* world = ecs_mini(); ECS_COMPONENT(world, Position); ecs_delete_with(world, ecs_id(Position)); ecs_entity_t e = ecs_new(world, Position); test_assert(e != 0); test_assert( ecs_has(world, e, Position)); const ecs_type_info_t *ti = ecs_get_type_info(world, ecs_id(Position)); test_assert(ti != NULL); test_int(ti->size, ECS_SIZEOF(Position)); test_int(ti->alignment, ECS_ALIGNOF(Position)); test_uint(ti->component, ecs_id(Position)); ecs_fini(world); } void World_no_name_prefix_after_init(void) { ecs_world_t *world = ecs_mini(); const ecs_world_info_t *info = ecs_get_world_info(world); test_assert(info->name_prefix == NULL); ecs_fini(world); } void World_set_get_context(void) { ecs_world_t *world = ecs_mini(); int ctx; ecs_set_ctx(world, &ctx, NULL); test_assert(ecs_get_ctx(world) == &ctx); test_assert(ecs_get_binding_ctx(world) == NULL); ecs_fini(world); } void World_set_get_binding_context(void) { ecs_world_t *world = ecs_mini(); int ctx; ecs_set_binding_ctx(world, &ctx, NULL); test_assert(ecs_get_ctx(world) == NULL); test_assert(ecs_get_binding_ctx(world) == &ctx); ecs_fini(world); } static void ctx_free(void *ptr) { *(int*)ptr = 10; } void World_set_get_context_w_free(void) { ecs_world_t *world = ecs_mini(); int ctx = 0; ecs_set_ctx(world, &ctx, ctx_free); test_assert(ecs_get_ctx(world) == &ctx); test_assert(ecs_get_binding_ctx(world) == NULL); test_int(ctx, 0); ecs_fini(world); test_int(ctx, 10); } void World_set_get_binding_context_w_free(void) { ecs_world_t *world = ecs_mini(); int ctx = 0; ecs_set_binding_ctx(world, &ctx, ctx_free); test_assert(ecs_get_ctx(world) == NULL); test_assert(ecs_get_binding_ctx(world) == &ctx); test_int(ctx, 0); ecs_fini(world); test_int(ctx, 10); }