#include <api.h>

void Hierarchies_setup(void) {
    ecs_log_set_level(-2);
}

void Hierarchies_empty_scope(void) {
    ecs_world_t *world = ecs_mini();

    test_assert(ecs_get_scope(world) == 0);

    ecs_fini(world);
}

void Hierarchies_get_parent(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Child, 0);

    ecs_entity_t e = ecs_get_target(world, Child, EcsChildOf, 0);
    test_assert(e == 0);

    ecs_fini(world);
}

void Hierarchies_get_parent_from_nested(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Scope, 0);
    ECS_ENTITY(world, Child, (ChildOf, Scope));

    ecs_entity_t e = ecs_get_target(world, Child, EcsChildOf, 0);
    test_assert(e == Scope);

    ecs_fini(world);
}

void Hierarchies_get_parent_from_nested_2(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Scope, 0);
    ECS_ENTITY(world, ChildScope, (ChildOf, Scope));
    ECS_ENTITY(world, Child, (ChildOf, Scope.ChildScope));

    ecs_entity_t e = ecs_get_target(world, Child, EcsChildOf, 0);
    test_assert(e == ChildScope);

    ecs_fini(world);
}

void Hierarchies_get_object_from_0(void) {
    install_test_abort();
    ecs_world_t *world = ecs_mini();

    test_expect_abort();
    ecs_get_target(world, 0, EcsChildOf, 0);
}

void Hierarchies_delete_children(void) {
    ecs_world_t *world = ecs_mini();

    ecs_entity_t parent = ecs_new(world, 0);
    test_assert(parent != 0);

    ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent);
    test_assert(child != 0);

    ecs_delete(world, parent);

    test_bool(ecs_exists(world, child), true);
    test_bool(ecs_is_alive(world, child), false);

    ecs_fini(world);
}

void Hierarchies_tree_iter_empty(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Parent, 0);

    ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t){ ecs_childof(Parent) });
    test_assert( !ecs_term_next(&it));

    ecs_fini(world);
}

void Hierarchies_tree_iter_1_table(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Parent, 0);

    ECS_ENTITY(world, Child1, (ChildOf, Parent));
    ECS_ENTITY(world, Child2, (ChildOf, Parent));
    ECS_ENTITY(world, Child3, (ChildOf, Parent));

    ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t){ ecs_childof(Parent) });
    test_assert( ecs_term_next(&it) == true);
    test_int( it.count, 3);

    test_assert(it.entities[0] == Child1);
    test_assert(it.entities[1] == Child2);
    test_assert(it.entities[2] == Child3);

    test_assert( !ecs_term_next(&it));

    ecs_fini(world);
}

void Hierarchies_tree_iter_2_tables(void) {
    ecs_world_t *world = ecs_mini();

    ECS_COMPONENT(world, Position);

    ECS_ENTITY(world, Parent, 0);

    ECS_ENTITY(world, Child1, (ChildOf, Parent));
    ECS_ENTITY(world, Child2, (ChildOf, Parent));
    ECS_ENTITY(world, Child3, (ChildOf, Parent), Position);
    ECS_ENTITY(world, Child4, (ChildOf, Parent), Position);

    ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t){ ecs_childof(Parent) });

    test_assert( ecs_term_next(&it) == true);
    test_int( it.count, 2);
    test_int(it.entities[0], Child1);
    test_int(it.entities[1], Child2);

    test_assert( ecs_term_next(&it) == true);
    test_int( it.count, 2);
    test_int(it.entities[0], Child3);
    test_int(it.entities[1], Child4);

    test_assert( !ecs_term_next(&it));

    ecs_fini(world);
}

void Hierarchies_path_depth_0(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Parent, 0);

    char *path = ecs_get_fullpath(world, Parent);
    test_str(path, "Parent");
    ecs_os_free(path);

    ecs_fini(world);
}

void Hierarchies_path_depth_1(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Parent, 0);
    ECS_ENTITY(world, Child, (ChildOf, Parent));

    char *path = ecs_get_fullpath(world, Child);
    test_str(path, "Parent.Child");
    ecs_os_free(path);

    ecs_fini(world);
}

void Hierarchies_path_depth_2(void) {
   ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Parent, 0);
    ECS_ENTITY(world, Child, (ChildOf, Parent));
    ECS_ENTITY(world, GrandChild, (ChildOf, Parent.Child));

    char *path = ecs_get_fullpath(world, GrandChild);
    test_str(path, "Parent.Child.GrandChild");
    ecs_os_free(path);

    ecs_fini(world);
}

void Hierarchies_path_core(void) {
    ecs_world_t *world = ecs_mini();

    char *path = ecs_get_path_w_sep(world, 0, ecs_id(EcsComponent), ".", 0);
    test_str(path, "Component");
    ecs_os_free(path);

    ecs_fini(world);
}

void Hierarchies_path_core_w_prefix(void) {
    ecs_world_t *world = ecs_mini();

    char *path = ecs_get_path_w_sep(world, 0, ecs_id(EcsComponent), ".", "::");
    test_str(path, "::flecs.core.Component");
    ecs_os_free(path);

    ecs_fini(world);
}

void Hierarchies_path_core_w_empty_prefix(void) {
    ecs_world_t *world = ecs_mini();

    char *path = ecs_get_path_w_sep(world, 0, ecs_id(EcsComponent), ".", "");
    test_str(path, "flecs.core.Component");
    ecs_os_free(path);

    ecs_fini(world);
}

void Hierarchies_path_this_w_empty_prefix(void) {
    ecs_world_t *world = ecs_mini();

    char *path = ecs_get_path_w_sep(world, 0, EcsThis, ".", "");
    test_str(path, "flecs.core.this");
    ecs_os_free(path);

    ecs_fini(world);
}

void Hierarchies_path_wildcard_w_empty_prefix(void) {
    ecs_world_t *world = ecs_mini();

    char *path = ecs_get_path_w_sep(world, 0, EcsWildcard, ".", "");
    test_str(path, "*");
    ecs_os_free(path);

    ecs_fini(world);
}

void Hierarchies_path_any_w_empty_prefix(void) {
    ecs_world_t *world = ecs_mini();

    char *path = ecs_get_path_w_sep(world, 0, EcsAny, ".", "");
    test_str(path, "_");
    ecs_os_free(path);

    ecs_fini(world);
}

void Hierarchies_rel_path_from_root(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Parent, 0);

    char *path = ecs_get_path(world, 0, Parent);
    test_str(path, "Parent");
    ecs_os_free(path);

    ecs_fini(world);
}

void Hierarchies_rel_path_from_self(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Parent, 0);

    char *path = ecs_get_path(world, Parent, Parent);
    test_str(path, "");
    ecs_os_free(path);

    ecs_fini(world);
}

void Hierarchies_rel_path_depth_1(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Parent, 0);
    ECS_ENTITY(world, Child, (ChildOf, Parent));

    char *path = ecs_get_path(world, Parent, Child);
    test_str(path, "Child");
    ecs_os_free(path);

    ecs_fini(world);
}

void Hierarchies_rel_path_depth_2(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Parent, 0);
    ECS_ENTITY(world, Child, (ChildOf, Parent));
    ECS_ENTITY(world, GrandChild, (ChildOf, Parent.Child));

    char *path = ecs_get_path(world, Parent, GrandChild);
    test_str(path, "Child.GrandChild");
    ecs_os_free(path);

    ecs_fini(world);
}

void Hierarchies_rel_path_no_match(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Parent, 0);
    ECS_ENTITY(world, Parent2, 0);
    ECS_ENTITY(world, Child, (ChildOf, Parent));

    char *path = ecs_get_path(world, Parent2, Child);
    test_str(path, "Parent.Child");
    ecs_os_free(path);

    ecs_fini(world);
}

void Hierarchies_path_custom_sep(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Parent, 0);
    ECS_ENTITY(world, Child, (ChildOf, Parent));

    char *path = ecs_get_path_w_sep(world, 0, Child, "::", NULL);
    test_str(path, "Parent::Child");
    ecs_os_free(path);

    ecs_fini(world);
}

void Hierarchies_path_custom_prefix(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Parent, 0);
    ECS_ENTITY(world, Child, (ChildOf, Parent));

    char *path = ecs_get_path_w_sep(world, 0, Child,  "::", "::");
    test_str(path, "::Parent::Child");
    ecs_os_free(path);

    ecs_fini(world);
}

void Hierarchies_path_prefix_rel_match(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Parent, 0);
    ECS_ENTITY(world, Child, (ChildOf, Parent));
    ECS_ENTITY(world, GrandChild, (ChildOf, Parent.Child));

    char *path = ecs_get_path_w_sep(world, Parent, GrandChild, "::", "::");
    test_str(path, "Child::GrandChild");
    ecs_os_free(path);

    ecs_fini(world);
}

void Hierarchies_path_prefix_rel_no_match(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Parent, 0);
    ECS_ENTITY(world, Parent2, 0);
    ECS_ENTITY(world, Child, (ChildOf, Parent));
    ECS_ENTITY(world, GrandChild, (ChildOf, Parent.Child));

    char *path = ecs_get_path_w_sep(world, Parent2, GrandChild, "::", "::");
    test_str(path, "::Parent::Child::GrandChild");
    ecs_os_free(path);

    ecs_fini(world);
}

void Hierarchies_path_w_number(void) {
    ecs_world_t *world = ecs_mini();

    ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, 1000);
    ecs_set_name(world, e, "Foo");

    char *path = ecs_get_fullpath(world, e);
    test_str(path, "1000.Foo");
    ecs_os_free(path);

    ecs_fini(world);
}


void Hierarchies_lookup_depth_0(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Parent, 0);

    ecs_entity_t e = ecs_lookup_fullpath(world, "Parent");
    test_assert(e == Parent);

    ecs_fini(world);
}

void Hierarchies_lookup_depth_1(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Parent, 0);
    ECS_ENTITY(world, Child, (ChildOf, Parent));

    ecs_entity_t e = ecs_lookup_fullpath(world, "Parent.Child");
    test_assert(e == Child);

    ecs_fini(world);
}

void Hierarchies_lookup_depth_2(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Parent, 0);
    ECS_ENTITY(world, Child, (ChildOf, Parent));
    ECS_ENTITY(world, GrandChild, (ChildOf, Parent.Child));

    ecs_entity_t e = ecs_lookup_fullpath(world, "Parent.Child.GrandChild");
    test_assert(e == GrandChild);

    ecs_fini(world);
}

void Hierarchies_lookup_rel_0(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Parent, 0);
    ECS_ENTITY(world, Child, (ChildOf, Parent));

    ecs_entity_t e = ecs_lookup_path(world, Parent, "Child");
    test_assert(e == Child);

    ecs_fini(world);
}

void Hierarchies_lookup_rel_1(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Parent, 0);
    ECS_ENTITY(world, Child, (ChildOf, Parent));
    ECS_ENTITY(world, GrandChild, (ChildOf, Parent.Child));

    ecs_entity_t e = ecs_lookup_path(world, Parent, "Child.GrandChild");
    test_assert(e == GrandChild);

    ecs_fini(world);
}

void Hierarchies_lookup_rel_2(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Parent, 0);
    ECS_ENTITY(world, Child, (ChildOf, Parent));
    ECS_ENTITY(world, GrandChild, (ChildOf, Parent.Child));
    ECS_ENTITY(world, GrandGrandChild, (ChildOf, Parent.Child.GrandChild));

    ecs_entity_t e = ecs_lookup_path(world, Parent, "Child.GrandChild.GrandGrandChild");
    test_assert(e == GrandGrandChild);

    ecs_fini(world);
}

void Hierarchies_lookup_custom_sep(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Parent, 0);
    ECS_ENTITY(world, Child, (ChildOf, Parent));

    ecs_entity_t e = ecs_lookup_path_w_sep(world, 0, "Parent::Child", "::", NULL, true);
    test_assert(e == Child);

    ecs_fini(world);
}

void Hierarchies_lookup_custom_prefix(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Parent, 0);
    ECS_ENTITY(world, Child, (ChildOf, Parent));

    ecs_entity_t e = ecs_lookup_path_w_sep(world, 0, "::Parent::Child", "::", "::", true);
    test_assert(e == Child);

    ecs_fini(world);
}

void Hierarchies_lookup_custom_prefix_from_root(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Parent, 0);
    ECS_ENTITY(world, Child, (ChildOf, Parent));

    ecs_entity_t e = ecs_lookup_path_w_sep(world, Parent, "::Parent::Child", "::", "::", true);
    test_assert(e == Child);

    ecs_fini(world);
}

void Hierarchies_lookup_self(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Parent, 0);

    ecs_entity_t e = ecs_lookup_path(world, Parent, "");
    test_assert(e == Parent);

    ecs_fini(world);
}

void Hierarchies_lookup_number(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Parent, 0);
    ECS_ENTITY(world, Parent2, 0);
    ECS_ENTITY(world, Child, (ChildOf, Parent));
    ECS_ENTITY(world, GrandChild, (ChildOf, Parent.Child));

    ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, 1000);
    ecs_set_name(world, e, "Foo");

    ecs_entity_t c = ecs_lookup_path(world, 0, "1000.Foo");
    test_assert(e == c);

    ecs_fini(world);
}

void Hierarchies_scope_set(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Scope, 0);

    ecs_entity_t old_scope = ecs_set_scope(world, Scope);
    test_assert(old_scope == 0);

    ecs_entity_t scope = ecs_get_scope(world);
    test_assert(scope == Scope);

    ecs_fini(world);
}

void Hierarchies_scope_set_w_new(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Scope, 0);

    ecs_entity_t old_scope = ecs_set_scope(world, Scope);
    test_assert(old_scope == 0);

    ecs_entity_t e = ecs_new(world, 0);
    test_assert(e != 0);
    test_assert(ecs_has_pair(world, e, EcsChildOf, Scope));

    old_scope = ecs_set_scope(world, 0);
    test_assert(old_scope == Scope);

    e = ecs_new(world, 0);
    test_assert(e != 0);
    test_assert(ecs_get_type(world, e) == NULL);

    ecs_fini(world);
}

void Hierarchies_scope_set_w_new_staged(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Scope, 0);

    ecs_defer_begin(world);

    ecs_entity_t old_scope = ecs_set_scope(world, Scope);
    test_assert(old_scope == 0);

    ecs_entity_t e1 = ecs_new(world, 0);
    test_assert(e1 != 0);

    old_scope = ecs_set_scope(world, 0);
    test_assert(old_scope == Scope);

    ecs_entity_t e2 = ecs_new(world, 0);
    test_assert(e2 != 0);

    ecs_defer_end(world);

    test_assert(ecs_has_pair(world, e1, EcsChildOf, Scope));
    test_assert(ecs_get_type(world, e2) == NULL);

    ecs_fini(world);
}

void Hierarchies_scope_set_again(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Scope, 0);
    ECS_ENTITY(world, ChildScope, 0);

    ecs_entity_t old_scope = ecs_set_scope(world, Scope);
    test_assert(old_scope == 0);

    old_scope = ecs_set_scope(world, ChildScope);
    test_assert(old_scope == Scope);

    ecs_fini(world);
}

void Hierarchies_scope_set_w_lookup(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Scope, 0);
    ECS_ENTITY(world, Child, (ChildOf, Scope));

    test_assert( ecs_lookup_fullpath(world, "Child") == 0);

    ecs_entity_t old_scope = ecs_set_scope(world, Scope);
    test_assert(old_scope == 0);

    ecs_entity_t e = ecs_lookup_fullpath(world, "Child");
    test_assert(e != 0);
    test_assert(e == Child);

    old_scope = ecs_set_scope(world, 0);
    test_assert(old_scope == Scope);

    test_assert( ecs_lookup_fullpath(world, "Child") == 0);

    ecs_fini(world);
}

void Hierarchies_lookup_in_parent_from_scope(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Scope, 0);
    ECS_ENTITY(world, ChildScope, (ChildOf, Scope));
    ECS_ENTITY(world, Child, (ChildOf, Scope));

    test_assert( ecs_lookup_fullpath(world, "Child") == 0);

    ecs_entity_t old_scope = ecs_set_scope(world, ChildScope);
    test_assert(old_scope == 0);

    ecs_entity_t e = ecs_lookup_fullpath(world, "Child");
    test_assert(e != 0);
    test_assert(e == Child);

    old_scope = ecs_set_scope(world, 0);
    test_assert(old_scope == ChildScope);

    test_assert( ecs_lookup_fullpath(world, "Child") == 0);

    ecs_fini(world);
}

void Hierarchies_lookup_in_root_from_scope(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Scope, 0);
    ECS_ENTITY(world, ChildScope, (ChildOf, Scope));
    ECS_ENTITY(world, Child, 0);

    ecs_entity_t old_scope = ecs_set_scope(world, ChildScope);
    test_assert(old_scope == 0);

    ecs_entity_t e = ecs_lookup_fullpath(world, "Child");
    test_assert(e != 0);
    test_assert(e == Child);

    old_scope = ecs_set_scope(world, 0);
    test_assert(old_scope == ChildScope);

    ecs_fini(world);
}

void Hierarchies_scope_component(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Scope, 0);

    ecs_entity_t old_scope = ecs_set_scope(world, Scope);
    test_assert(old_scope == 0);

    ECS_COMPONENT(world, Position);

    ecs_entity_t e = ecs_lookup_fullpath(world, "Position");
    test_assert(e != 0);
    test_assert(e == ecs_id(Position));

    old_scope = ecs_set_scope(world, 0);
    test_assert(old_scope == Scope);

    e = ecs_lookup_fullpath(world, "Position");
    test_assert(e == 0);

    e = ecs_lookup_fullpath(world, "Scope.Position");
    test_assert(e == ecs_id(Position));

    ecs_fini(world);
}

void Hierarchies_scope_component_no_macro(void) {
    ecs_world_t *world = ecs_mini();

    ecs_entity_t Scope = ecs_new_id(world);

    ecs_entity_t old_scope = ecs_set_scope(world, Scope);
    test_assert(old_scope == 0);

    ecs_entity_t e2 = ecs_entity_init(world, &(ecs_entity_desc_t){
        .use_low_id = true
    });

    test_assert(e2 != 0);
    test_assert(ecs_has_pair(world, e2, EcsChildOf, Scope));


    ecs_fini(world);
    
    /* The real purpose of this test is to catch an issue where a component was
     * added to multiple tables, which caused a crash during ecs_fini */
}

void Hierarchies_fullpath_for_core(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Parent, 0);
    ECS_ENTITY(world, Child, (ChildOf, Parent));

    char *path = ecs_get_fullpath(world, ecs_id(EcsComponent));
    test_str(path, "Component");
    ecs_os_free(path);

    ecs_fini(world);
}

void Hierarchies_new_from_path_depth_0(void) {
    ecs_world_t *world = ecs_mini();

    ecs_entity_t e = ecs_new_from_path(world, 0, "foo");
    test_assert(e != 0);
    test_str(ecs_get_name(world, e), "foo");

    const ecs_type_t *type = ecs_get_type(world, e);
    test_assert(type != NULL);
    test_int(type->count, 1);

    char *path = ecs_get_fullpath(world, e);
    test_str(path, "foo");
    ecs_os_free(path);

    ecs_fini(world);
}

void Hierarchies_new_from_path_depth_1(void) {
    ecs_world_t *world = ecs_mini();

    ecs_entity_t e = ecs_new_from_path(world, 0, "foo.bar");
    test_assert(e != 0);
    test_str(ecs_get_name(world, e), "bar");

    const ecs_type_t *type = ecs_get_type(world, e);
    test_assert(type != NULL);
    test_int(type->count, 2);

    char *path = ecs_get_fullpath(world, e);
    test_str(path, "foo.bar");
    ecs_os_free(path);

    ecs_fini(world);
}

void Hierarchies_new_from_path_depth_2(void) {
    ecs_world_t *world = ecs_mini();

    ecs_entity_t e = ecs_new_from_path(world, 0, "foo.bar.hello");
    test_assert(e != 0);
    test_str(ecs_get_name(world, e), "hello");

    const ecs_type_t *type = ecs_get_type(world, e);
    test_assert(type != NULL);
    test_int(type->count, 2);

    char *path = ecs_get_fullpath(world, e);
    test_str(path, "foo.bar.hello");
    ecs_os_free(path);

    ecs_fini(world);
}


void Hierarchies_new_from_path_existing_depth_0(void) {
    ecs_world_t *world = ecs_mini();

    ecs_entity_t e = ecs_new_from_path(world, 0, "foo");
    test_assert(e != 0);
    test_str(ecs_get_name(world, e), "foo");

    ecs_entity_t f = ecs_new_from_path(world, 0, "foo");
    test_assert(f != 0);
    test_assert(f == e);
    test_str(ecs_get_name(world, e), "foo");

    ecs_fini(world);
}

void Hierarchies_new_from_path_existing_depth_1(void) {
    ecs_world_t *world = ecs_mini();

    ecs_entity_t e = ecs_new_from_path(world, 0, "foo.bar");
    test_assert(e != 0);
    test_str(ecs_get_name(world, e), "bar");

    ecs_entity_t f = ecs_new_from_path(world, 0, "foo.bar");
    test_assert(f != 0);
    test_assert(f == e);
    test_str(ecs_get_name(world, e), "bar");

    ecs_fini(world);
}

void Hierarchies_new_from_path_existing_depth_2(void) {
    ecs_world_t *world = ecs_mini();

    ecs_entity_t e = ecs_new_from_path(world, 0, "foo.bar.hello");
    test_assert(e != 0);
    test_str(ecs_get_name(world, e), "hello");

    ecs_entity_t f = ecs_new_from_path(world, 0, "foo.bar.hello");
    test_assert(f != 0);
    test_assert(f == e);
    test_str(ecs_get_name(world, e), "hello");

    ecs_fini(world);
}

void Hierarchies_add_path_depth_0(void) {
    ecs_world_t *world = ecs_mini();

    ecs_entity_t id = ecs_new(world, 0);
    test_assert(id != 0);

    ecs_entity_t e = ecs_add_path(world, id, 0, "foo");
    test_assert(e != 0);
    test_assert(e == id);
    test_str(ecs_get_name(world, e), "foo");

    const ecs_type_t *type = ecs_get_type(world, e);
    test_assert(type != NULL);
    test_int(type->count, 1);

    char *path = ecs_get_fullpath(world, e);
    test_str(path, "foo");
    ecs_os_free(path);

    ecs_fini(world);
}

void Hierarchies_add_path_depth_1(void) {
    ecs_world_t *world = ecs_mini();

    ecs_entity_t id = ecs_new(world, 0);
    test_assert(id != 0);

    ecs_entity_t e = ecs_add_path(world, id, 0, "foo.bar");
    test_assert(e != 0);
    test_assert(e == id);
    test_str(ecs_get_name(world, e), "bar");

    const ecs_type_t *type = ecs_get_type(world, e);
    test_assert(type != NULL);
    test_int(type->count, 2);

    char *path = ecs_get_fullpath(world, e);
    test_str(path, "foo.bar");
    ecs_os_free(path);

    ecs_fini(world);
}

void Hierarchies_add_path_depth_2(void) {
    ecs_world_t *world = ecs_mini();

    ecs_entity_t id = ecs_new(world, 0);
    test_assert(id != 0);

    ecs_entity_t e = ecs_add_path(world, id, 0, "foo.bar.hello");
    test_assert(e != 0);
    test_assert(e == id);
    test_str(ecs_get_name(world, e), "hello");

    const ecs_type_t *type = ecs_get_type(world, e);
    test_assert(type != NULL);
    test_int(type->count, 2);

    char *path = ecs_get_fullpath(world, e);
    test_str(path, "foo.bar.hello");
    ecs_os_free(path);

    ecs_fini(world);
}

void Hierarchies_add_path_existing_depth_0(void) {
    install_test_abort();

    ecs_world_t *world = ecs_mini();

    ecs_entity_t e = ecs_new_from_path(world, 0, "foo");
    test_assert(e != 0);
    test_str(ecs_get_name(world, e), "foo");    

    ecs_entity_t id = ecs_new(world, 0);
    test_assert(id != 0);

    test_expect_abort();
    ecs_add_path(world, id, 0, "foo");
}

void Hierarchies_add_path_existing_depth_1(void) {
    install_test_abort();
    ecs_world_t *world = ecs_mini();

    ecs_entity_t e = ecs_new_from_path(world, 0, "foo.bar");
    test_assert(e != 0);
    test_str(ecs_get_name(world, e), "bar");    

    ecs_entity_t id = ecs_new(world, 0);
    test_assert(id != 0);

    test_expect_abort();
    ecs_add_path(world, id, 0, "foo.bar");
}

void Hierarchies_add_path_existing_depth_2(void) {
    install_test_abort();
    ecs_world_t *world = ecs_mini();

    ecs_entity_t e = ecs_new_from_path(world, 0, "foo.bar.hello");
    test_assert(e != 0);
    test_str(ecs_get_name(world, e), "hello");    

    ecs_entity_t id = ecs_new(world, 0);
    test_assert(id != 0);

    test_expect_abort();
    ecs_add_path(world, id, 0, "foo.bar.hello");
}

void Hierarchies_add_path_from_scope(void) {
    ecs_world_t *world = ecs_mini();

    ecs_entity_t e = ecs_new_from_path(world, 0, "foo.bar");
    test_assert(e != 0);
    test_str(ecs_get_name(world, e), "bar");    

    ecs_set_scope(world, e);

    ecs_entity_t id = ecs_new_id(world);
    test_assert(id != 0);

    ecs_entity_t f = ecs_add_path(world, id, 0, "hello.world");
    test_assert(f != 0);
    test_str(ecs_get_name(world, f), "world");

    char *path = ecs_get_fullpath(world, f);
    test_str(path, "hello.world");
    ecs_os_free(path);

    ecs_set_scope(world, 0);

    ecs_fini(world);
}

void Hierarchies_add_path_from_scope_new_entity(void) {
    ecs_world_t *world = ecs_mini();

    ecs_entity_t e = ecs_new_from_path(world, 0, "foo.bar");
    test_assert(e != 0);
    test_str(ecs_get_name(world, e), "bar");    

    ecs_set_scope(world, e);

    ecs_entity_t id = ecs_add_path(world, 0, 0, "hello.world");
    test_assert(id != 0);
    test_str(ecs_get_name(world, id), "world");

    char *path = ecs_get_fullpath(world, id);
    test_str(path, "foo.bar.hello.world");
    ecs_os_free(path);

    ecs_set_scope(world, 0);

    ecs_fini(world);
}

void Hierarchies_add_root_path_to_child(void) {
    ecs_world_t *world = ecs_mini();

    ecs_entity_t parent = ecs_new_entity(world, "parent");
    ecs_entity_t child = ecs_new_entity(world, "parent.child");
    test_assert(ecs_get_target(world, child, EcsChildOf, 0) == parent);
    test_str(ecs_get_name(world, child), "child");

    ecs_add_path_w_sep(world, child, 0, "::child", ".", "::");
    test_assert(ecs_get_target(world, child, EcsChildOf, 0) == 0);
    test_str(ecs_get_name(world, child), "child");

    ecs_fini(world);
}

void Hierarchies_add_parent_path_from_root_to_child(void) {
    ecs_world_t *world = ecs_mini();

    ecs_entity_t gparent = ecs_new_entity(world, "gparent");
    ecs_entity_t parent = ecs_new_entity(world, "gparent.parent");
    test_assert(ecs_get_target(world, parent, EcsChildOf, 0) == gparent);
    ecs_entity_t child = ecs_new_entity(world, "gparent.parent.child");
    test_assert(ecs_get_target(world, child, EcsChildOf, 0) == parent);
    test_str(ecs_get_name(world, child), "child");

    ecs_add_path_w_sep(world, child, 0, "::gparent.child", ".", "::");
    test_assert(ecs_get_target(world, child, EcsChildOf, 0) == gparent);
    test_str(ecs_get_name(world, child), "child");

    ecs_fini(world);
}

void Hierarchies_new_w_child_in_root(void) {
    ecs_world_t *world = ecs_mini();

    ecs_entity_t scope = ecs_new(world, 0);
    ecs_entity_t parent = ecs_new(world, 0);
    
    ecs_set_scope(world, scope);

    ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent);
    test_assert( ecs_has_pair(world, child, EcsChildOf, parent));
    test_assert( !ecs_has_pair(world, child, EcsChildOf, scope));

    ecs_entity_t child_2 = ecs_new(world, 0);
    test_assert( !ecs_has_pair(world, child_2, EcsChildOf, parent));
    test_assert( ecs_has_pair(world, child_2, EcsChildOf, scope));

    ecs_fini(world);
}

void Hierarchies_delete_child(void) {
    ecs_world_t *world = ecs_mini();

    ecs_entity_t parent = ecs_new(world, 0);
    test_assert(parent != 0);

    ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent);
    test_assert(ecs_get_type(world, child) != NULL);

    ecs_delete(world, parent);
    
    test_bool(ecs_exists(world, parent), true);
    test_bool(ecs_exists(world, child), true);    
    test_bool(ecs_is_alive(world, parent), false);
    test_bool(ecs_is_alive(world, child), false);
    
    ecs_fini(world);
}

void Hierarchies_delete_2_children(void) {
    ecs_world_t *world = ecs_mini();

    ecs_entity_t parent = ecs_new(world, 0);
    test_assert(parent != 0);

    ecs_entity_t child_1 = ecs_new_w_pair(world, EcsChildOf, parent);
    test_assert(child_1 != 0);
    test_assert(ecs_get_type(world, child_1) != NULL);

    ecs_entity_t child_2 = ecs_new_w_pair(world, EcsChildOf, parent);
    test_assert(child_2 != 0);
    test_assert(ecs_get_type(world, child_2) != NULL);

    ecs_delete(world, parent);
    
    test_bool(ecs_is_alive(world, parent), false);
    test_bool(ecs_is_alive(world, child_1), false);
    test_bool(ecs_is_alive(world, child_2), false);

    ecs_fini(world);
}

void Hierarchies_delete_2_children_different_type(void) {
    ecs_world_t *world = ecs_mini();

    ECS_COMPONENT(world, Position);
    ECS_COMPONENT(world, Velocity);

    ecs_entity_t parent = ecs_new(world, 0);
    test_assert(parent != 0);

    ecs_entity_t child_1 = ecs_new_w_pair(world, EcsChildOf, parent);
    test_assert(child_1 != 0);
    test_assert(ecs_get_type(world, child_1) != NULL);
    ecs_add(world, child_1, Position);

    ecs_entity_t child_2 = ecs_new_w_pair(world, EcsChildOf, parent);
    test_assert(child_2 != 0);
    test_assert(ecs_get_type(world, child_2) != NULL);
    ecs_add(world, child_2, Velocity);

    ecs_delete(world, parent);
    
    test_bool(ecs_is_alive(world, parent), false);
    test_bool(ecs_is_alive(world, child_1), false);
    test_bool(ecs_is_alive(world, child_2), false);

    ecs_fini(world);
}

void Hierarchies_delete_tree_2_levels(void) {
    ecs_world_t *world = ecs_mini();

    ecs_entity_t parent = ecs_new(world, 0);
    test_assert(parent != 0);

    ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent);
    test_assert(ecs_get_type(world, child) != NULL);

    ecs_entity_t grand_child = ecs_new_w_pair(world, EcsChildOf, child);
    test_assert(ecs_get_type(world, grand_child) != NULL);

    ecs_delete(world, parent);
    
    test_bool(ecs_is_alive(world, parent), false);
    test_bool(ecs_is_alive(world, child), false);
    test_bool(ecs_is_alive(world, grand_child), false);

    ecs_fini(world);
}

void Hierarchies_delete_tree_3_levels(void) {
    ecs_world_t *world = ecs_mini();

    ecs_entity_t parent = ecs_new(world, 0);
    test_assert(parent != 0);

    ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent);
    test_assert(ecs_get_type(world, child) != NULL);

    ecs_entity_t grand_child = ecs_new_w_pair(world, EcsChildOf, child);
    test_assert(ecs_get_type(world, grand_child) != NULL);

    ecs_entity_t great_grand_child = ecs_new_w_pair(world, EcsChildOf, grand_child);
    test_assert(ecs_get_type(world, great_grand_child) != NULL);    

    ecs_delete(world, parent);
    
    test_bool(ecs_is_alive(world, parent), false);
    test_bool(ecs_is_alive(world, child), false);
    test_bool(ecs_is_alive(world, grand_child), false);
    test_bool(ecs_is_alive(world, great_grand_child), false);

    ecs_fini(world);
}

void Hierarchies_delete_tree_count_tables(void) {
    ecs_world_t *world = ecs_mini();

    ECS_COMPONENT(world, Position);

    ecs_entity_t parent = ecs_new(world, Position);
    test_assert(parent != 0);

    ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent);
    test_assert(ecs_get_type(world, child) != NULL);
    ecs_add(world, child, Position);

    ecs_entity_t grand_child = ecs_new_w_pair(world, EcsChildOf, child);
    test_assert(ecs_get_type(world, grand_child) != NULL);
    ecs_add(world, grand_child, Position);

    ecs_query_t *q = ecs_query_new(world, "Position");
    ecs_iter_t it = ecs_query_iter(world, q);
    test_int(it.table_count, 3);
    ecs_iter_fini(&it);

    ecs_delete(world, parent);
    
    test_bool(ecs_is_alive(world, parent), false);
    test_bool(ecs_is_alive(world, child), false);
    test_bool(ecs_is_alive(world, grand_child), false);

    it = ecs_query_iter(world, q);
    test_int(it.table_count, 0);
    ecs_iter_fini(&it);

    ecs_fini(world);
}

void Hierarchies_delete_tree_staged(void) {
    ecs_world_t *world = ecs_mini();

    ECS_COMPONENT(world, Position);

    ecs_entity_t parent = ecs_new(world, Position);
    test_assert(parent != 0);

    ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent);
    test_assert(ecs_get_type(world, child) != NULL);
    ecs_add(world, child, Position);

    ecs_entity_t grand_child = ecs_new_w_pair(world, EcsChildOf, child);
    test_assert(ecs_get_type(world, grand_child) != NULL);
    ecs_add(world, grand_child, Position);

    ecs_query_t *q = ecs_query_new(world, "Position");
    ecs_iter_t it = ecs_query_iter(world, q);
    test_int(it.table_count, 3);
    ecs_iter_fini(&it);

    ecs_defer_begin(world);
    ecs_delete(world, parent);
    ecs_defer_end(world);
    
    test_bool(ecs_is_alive(world, parent), false);
    test_bool(ecs_is_alive(world, child), false);
    test_bool(ecs_is_alive(world, grand_child), false);

    it = ecs_query_iter(world, q);
    test_int(it.table_count, 0);
    ecs_iter_fini(&it);

    ecs_fini(world);
}

void Hierarchies_delete_tree_empty_table(void) {
    ecs_world_t *world = ecs_mini();

    ECS_COMPONENT(world, Position);

    ECS_ENTITY(world, Parent, 0);
    ECS_ENTITY(world, Child, Position, (ChildOf, Parent));

    ecs_delete(world, Parent);
    
    test_assert(!ecs_is_alive(world, Parent));
    test_assert(!ecs_is_alive(world, Child));

    ecs_fini(world);
}

void Hierarchies_delete_tree_recreate(void) {
    ecs_world_t *world = ecs_mini();

    ECS_COMPONENT(world, Position);

    ecs_entity_t parent = ecs_new(world, 0);
    ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent);
    test_assert(parent != 0);
    test_assert(child != 0);
    test_assert(ecs_has_pair(world, child, EcsChildOf, parent));

    ecs_delete_children(world, parent);

    ecs_new(world, Position);

    ecs_entity_t child_2  = ecs_new_w_pair(world, EcsChildOf, parent);
    test_assert(child_2 != 0);
    test_assert(ecs_has_pair(world, child_2, EcsChildOf, parent));

    ecs_fini(world);
}

void Hierarchies_get_child_count(void) {
    ecs_world_t *world = ecs_mini();

    ECS_COMPONENT(world, Position);

    ECS_ENTITY(world, Parent, 0);
    ECS_ENTITY(world, Child1, (ChildOf, Parent), Position);
    ECS_ENTITY(world, Child2, (ChildOf, Parent), Position);
    ECS_ENTITY(world, Child3, (ChildOf, Parent), Position);

    test_int(ecs_count_id(world, ecs_childof(Parent)), 3);

    ecs_fini(world);
}

void Hierarchies_get_child_count_no_children(void) {
    ecs_world_t *world = ecs_mini();

    ECS_ENTITY(world, Parent, 0);

    test_int(ecs_count_id(world, ecs_childof(Parent)), 0);

    ecs_fini(world);
}

void Hierarchies_get_child_count_2_tables(void) {
    ecs_world_t *world = ecs_mini();

    ECS_COMPONENT(world, Position);
    ECS_COMPONENT(world, Velocity);

    ECS_ENTITY(world, Parent, 0);
    ECS_ENTITY(world, Child1, (ChildOf, Parent), Position);
    ECS_ENTITY(world, Child2, (ChildOf, Parent), Position);
    ECS_ENTITY(world, Child3, (ChildOf, Parent), Position);
    ECS_ENTITY(world, Child4, (ChildOf, Parent), Velocity);

    test_int(ecs_count_id(world, ecs_childof(Parent)), 4);

    ecs_fini(world);
}

void Hierarchies_scope_iter_after_delete_tree(void) {
    ecs_world_t *world = ecs_mini();

    ECS_COMPONENT(world, Position);

    ecs_entity_t parent = ecs_new(world, 0);
    ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent);
    test_assert(parent != 0);
    test_assert(child != 0);
    test_assert(ecs_has_pair(world, child, EcsChildOf, parent));

    ecs_delete_children(world, parent);

    ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t){ ecs_childof(parent) });
    test_assert(!ecs_term_next(&it));

    ecs_fini(world);
}

void Hierarchies_add_child_after_delete_tree(void) {
    ecs_world_t *world = ecs_mini();

    ECS_COMPONENT(world, Position);

    ecs_entity_t parent = ecs_new(world, 0);
    ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent);
    test_assert(parent != 0);
    test_assert(child != 0);
    test_assert(ecs_has_pair(world, child, EcsChildOf, parent));

    ecs_delete_children(world, parent);

    child = ecs_new_w_pair(world, EcsChildOf, parent);

    ecs_fini(world);
}

static int on_remove_count = 0;

static
void RemovePosition(ecs_iter_t *it) {
    int i;
    for (i = 0; i < it->count; i ++) {
        on_remove_count ++;
    }
}

void Hierarchies_delete_tree_w_onremove(void) {
    ecs_world_t *world = ecs_mini();

    ECS_COMPONENT(world, Position);

    ECS_OBSERVER(world, RemovePosition, EcsOnRemove, Position);

    ecs_entity_t parent = ecs_new(world, 0);
    test_assert(parent != 0);
    ecs_entity_t child_1 = ecs_new_w_pair(world, EcsChildOf, parent);
    ecs_entity_t child_2 = ecs_new_w_pair(world, EcsChildOf, parent);
    test_assert(child_1 != 0);
    test_assert(child_2 != 0);
    test_assert(ecs_has_pair(world, child_1, EcsChildOf, parent));
    test_assert(ecs_has_pair(world, child_2, EcsChildOf, parent));

    ecs_add(world, parent, Position);
    ecs_add(world, child_1, Position);
    ecs_add(world, child_2, Position);

    ecs_delete(world, parent);

    test_assert( !ecs_is_alive(world, parent));
    test_assert( !ecs_is_alive(world, child_1));
    test_assert( !ecs_is_alive(world, child_2));

    test_int(on_remove_count, 3);

    ecs_fini(world);
}

static int dtor_count = 0;

static ECS_DTOR(Position, ptr, {
    dtor_count ++;
})

void Hierarchies_delete_tree_w_dtor(void) {
    ecs_world_t *world = ecs_mini();

    ECS_COMPONENT(world, Position);

    ecs_set_hooks(world, Position, {
        .dtor = ecs_dtor(Position)
    });

    ecs_entity_t parent = ecs_new(world, 0);
    test_assert(parent != 0);
    ecs_entity_t child_1 = ecs_new_w_pair(world, EcsChildOf, parent);
    ecs_entity_t child_2 = ecs_new_w_pair(world, EcsChildOf, parent);
    test_assert(child_1 != 0);
    test_assert(child_2 != 0);
    test_assert(ecs_has_pair(world, child_1, EcsChildOf, parent));
    test_assert(ecs_has_pair(world, child_2, EcsChildOf, parent));

    ecs_add(world, parent, Position);
    ecs_add(world, child_1, Position);
    ecs_add(world, child_2, Position);

    ecs_delete(world, parent);

    test_assert( !ecs_is_alive(world, parent));
    test_assert( !ecs_is_alive(world, child_1));
    test_assert( !ecs_is_alive(world, child_2));

    test_int(dtor_count, 3);

    ecs_fini(world);
}

void Hierarchies_add_child_to_recycled_parent(void) {
    ecs_world_t *world = ecs_mini();

    ECS_COMPONENT(world, Position);

    ecs_entity_t parent = ecs_new(world, 0);
    test_assert(parent != 0);

    ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent);
    test_assert(child != 0);
    test_assert(ecs_has_pair(world, child, EcsChildOf, parent));

    ecs_delete(world, parent);
    test_assert( !ecs_is_alive(world, parent));
    test_assert( !ecs_is_alive(world, child));

    ecs_entity_t new_parent = ecs_new(world, 0);
    test_assert(new_parent != 0);
    /* Make sure we have a recycled identifier */
    test_assert(ecs_entity_t_lo(new_parent) != new_parent);

    child = ecs_new_w_pair(world, EcsChildOf, new_parent);
    test_assert(child != 0);
    test_assert(ecs_has_pair(world, child, EcsChildOf, new_parent));
    test_assert( ecs_is_alive(world, child));
    test_assert( ecs_is_alive(world, child));

    ecs_fini(world);
}

void Hierarchies_get_type_after_recycled_parent_add(void) {
    ecs_world_t *world = ecs_mini();

    ECS_COMPONENT(world, Position);

    ecs_entity_t parent = ecs_new(world, 0);
    test_assert(parent != 0);
    test_assert( ecs_get_type(world, parent) == NULL);

    ecs_delete(world, parent);
    test_assert( !ecs_is_alive(world, parent));

    parent = ecs_new(world, Position);
    test_assert(parent != 0);
    test_assert(ecs_entity_t_lo(parent) != parent); // Ensure recycled
    test_assert( ecs_get_type(world, parent) != NULL);

    ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, parent);
    test_assert(e != 0);
    test_assert( ecs_get_type(world, parent) != NULL);

    ecs_fini(world);
}

void Hierarchies_rematch_after_add_to_recycled_parent(void) {
    ecs_world_t *world = ecs_mini();
    
    ECS_COMPONENT(world, Position);
    ECS_TAG(world, Tag);

    ecs_query_t *q = ecs_query_new(world, "Tag, Position(parent)");

    ecs_entity_t parent = ecs_new(world, 0);
    test_assert(parent != 0);

    ecs_delete(world, parent);
    test_assert( !ecs_is_alive(world, parent));

    parent = ecs_new(world, 0);
    test_assert(parent != 0);

    ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, parent);
    test_assert(e != 0);
    test_assert( ecs_has_pair(world, e, EcsChildOf, parent));
    ecs_add(world, e, Tag);

    ecs_iter_t it = ecs_query_iter(world, q);
    test_bool(ecs_query_next(&it), false);

    ecs_set(world, parent, Position, {10, 20});

    ecs_run_aperiodic(world, 0);

    it = ecs_query_iter(world, q);
    test_bool(ecs_query_next(&it), true);
    test_int(it.count, 1);

    const Position *p = ecs_field(&it, Position, 2);
    test_assert(p != NULL);
    test_int(p->x, 10);
    test_int(p->y, 20);

    test_assert(ecs_field_src(&it, 2) == parent);
    test_bool(ecs_query_next(&it), false);

    ecs_fini(world);
}

void Hierarchies_cascade_after_recycled_parent_change(void) {
    ecs_world_t *world = ecs_mini();
    
    ECS_COMPONENT(world, Position);
    ECS_TAG(world, Tag);

    ecs_query_t *q = ecs_query_new(world, "Tag, ?Position(parent|cascade)");

    ecs_entity_t parent = ecs_new(world, 0);
    test_assert(parent != 0);
    ecs_entity_t child = ecs_new(world, 0);
    test_assert(child != 0);

    ecs_delete(world, parent);
    test_assert( !ecs_is_alive(world, parent));
    ecs_delete(world, child);
    test_assert( !ecs_is_alive(world, child));

    parent = ecs_new_w_id(world, Tag);
    test_assert(parent != 0);
    child = ecs_new_w_id(world, Tag);
    test_assert(child != 0);
    ecs_add_pair(world, child, EcsChildOf, parent);

    ecs_entity_t e = ecs_new_w_id(world, Tag);
    test_assert(e != 0);

    ecs_add_pair(world, e, EcsChildOf, child);
    test_assert( ecs_has_pair(world, e, EcsChildOf, child));

    ecs_iter_t it = ecs_query_iter(world, q);
    test_bool(ecs_query_next(&it), true);
    test_int(it.count, 1);
    test_assert(it.entities[0] == parent);
    test_assert(ecs_field_src(&it, 2) == 0);
    const Position *p = ecs_field(&it, Position, 2);
    test_assert(p == NULL);

    test_bool(ecs_query_next(&it), true);
    test_int(it.count, 1);
    test_assert(it.entities[0] == child);
    test_assert(ecs_field_src(&it, 2) == 0);
    p = ecs_field(&it, Position, 2);
    test_assert(p == NULL);

    test_bool(ecs_query_next(&it), true);
    test_int(it.count, 1);
    test_assert(it.entities[0] == e);
    test_assert(ecs_field_src(&it, 2) == 0);
    p = ecs_field(&it, Position, 2);
    test_assert(p == NULL);

    test_bool(ecs_query_next(&it), false);

    ecs_set(world, parent, Position, {10, 20});
    ecs_set(world, child, Position, {20, 30});

    ecs_run_aperiodic(world, 0);

    it = ecs_query_iter(world, q);
    test_bool(ecs_query_next(&it), true);
    test_int(it.count, 1);
    test_assert(it.entities[0] == parent);
    test_assert(ecs_field_src(&it, 2) == 0);
    p = ecs_field(&it, Position, 2);
    test_assert(p == NULL);

    test_bool(ecs_query_next(&it), true);
    test_int(it.count, 1);
    test_assert(it.entities[0] == child);
    test_assert(ecs_field_src(&it, 2) == parent);
    p = ecs_field(&it, Position, 2);
    test_assert(p != NULL);
    test_int(p->x, 10);
    test_int(p->y, 20);

    test_bool(ecs_query_next(&it), true);
    test_int(it.count, 1);
    test_assert(it.entities[0] == e);
    test_assert(ecs_field_src(&it, 2) == child);
    p = ecs_field(&it, Position, 2);
    test_assert(p != NULL);
    test_int(p->x, 20);
    test_int(p->y, 30);

    test_bool(ecs_query_next(&it), false);

    ecs_fini(world);
}

void Hierarchies_long_name_depth_0(void) {
    ecs_world_t *world = ecs_mini();

    const char *parent_name = "a_parent_entity_with_an_identifier_longer_than_sixty_four_characters";
    test_assert(strlen(parent_name) >= 64);

    ecs_entity_t parent = ecs_entity_init(world, &(ecs_entity_desc_t){
        .name = parent_name
    });

    test_str(ecs_get_name(world, parent), parent_name);

    ecs_entity_t e = ecs_lookup_fullpath(world, parent_name);
    test_assert(e == parent);

    char *path = ecs_get_fullpath(world, e);
    test_str(path, parent_name);
    ecs_os_free(path);

    ecs_fini(world);
}

void Hierarchies_long_name_depth_1(void) {
    ecs_world_t *world = ecs_mini();

    const char *parent_name = 
        "a_parent_entity_with_an_identifier_longer_than_sixty_four_characters";
    test_assert(strlen(parent_name) >= 64);
    ecs_entity_t parent = ecs_entity_init(world, &(ecs_entity_desc_t){
        .name = parent_name
    });
    test_str(ecs_get_name(world, parent), parent_name);

    const char *child_name = 
        "a_child_entity_with_an_identifier_longer_than_sixty_four_characters";
    test_assert(strlen(child_name) >= 64);
    ecs_entity_t child = ecs_entity_init(world, &(ecs_entity_desc_t){
        .name = child_name,
        .add = { ecs_pair(EcsChildOf, parent) }
    });    
    test_str(ecs_get_name(world, child), child_name);

    const char *search_path = 
      "a_parent_entity_with_an_identifier_longer_than_sixty_four_characters."
      "a_child_entity_with_an_identifier_longer_than_sixty_four_characters";

    ecs_entity_t e = ecs_lookup_fullpath(world, search_path);
    test_assert(e == child);

    char *path = ecs_get_fullpath(world, e);
    test_str(path, search_path);
    ecs_os_free(path);

    ecs_fini(world);
}

void Hierarchies_long_name_depth_2(void) {
    ecs_world_t *world = ecs_mini();

    const char *parent_name = 
        "a_parent_entity_with_an_identifier_longer_than_sixty_four_characters";
    test_assert(strlen(parent_name) >= 64);
    ecs_entity_t parent = ecs_entity_init(world, &(ecs_entity_desc_t){
        .name = parent_name
    });
    test_str(ecs_get_name(world, parent), parent_name);

    const char *child_name = 
        "a_child_entity_with_an_identifier_longer_than_sixty_four_characters";
    test_assert(strlen(child_name) >= 64);
    ecs_entity_t child = ecs_entity_init(world, &(ecs_entity_desc_t){
        .name = child_name,
        .add = { ecs_pair(EcsChildOf, parent) }
    });    
    test_str(ecs_get_name(world, child), child_name);

    const char *grand_child_name = 
        "a_grand_child_entity_with_an_identifier_longer_than_sixty_four_characters";
    test_assert(strlen(grand_child_name) >= 64);
    ecs_entity_t grand_child = ecs_entity_init(world, &(ecs_entity_desc_t){
        .name = grand_child_name,
        .add = { ecs_pair(EcsChildOf, child) }
    });    
    test_str(ecs_get_name(world, grand_child), grand_child_name);

    const char *search_path = 
      "a_parent_entity_with_an_identifier_longer_than_sixty_four_characters."
      "a_child_entity_with_an_identifier_longer_than_sixty_four_characters."
      "a_grand_child_entity_with_an_identifier_longer_than_sixty_four_characters";

    ecs_entity_t e = ecs_lookup_fullpath(world, search_path);
    test_assert(e == grand_child);

    char *path = ecs_get_fullpath(world, e);
    test_str(path, search_path);
    ecs_os_free(path);

    ecs_fini(world);
}

void Hierarchies_ensure_1_parent_after_adding_2(void) {
    ecs_world_t *world = ecs_mini();

    ecs_entity_t parent_1 = ecs_new(world, 0);
    test_assert(parent_1 != 0);

    ecs_entity_t parent_2 = ecs_new(world, 0);
    test_assert(parent_2 != 0);

    ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent_1);
    test_assert(child != 0);
    test_assert( ecs_has_pair(world, child, EcsChildOf, parent_1));

    ecs_add_pair(world, child, EcsChildOf, parent_2);
    test_assert( ecs_has_pair(world, child, EcsChildOf, parent_2));
    test_assert( !ecs_has_pair(world, child, EcsChildOf, parent_1));

    ecs_fini(world);
}

void Hierarchies_ensure_child_alive_after_deleting_prev_parent(void) {
    ecs_world_t *world = ecs_mini();

    ecs_entity_t parent_1 = ecs_new(world, 0);
    test_assert(parent_1 != 0);

    ecs_entity_t parent_2 = ecs_new(world, 0);
    test_assert(parent_2 != 0);

    ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent_1);
    test_assert(child != 0);
    test_assert( ecs_has_pair(world, child, EcsChildOf, parent_1));

    ecs_add_pair(world, child, EcsChildOf, parent_2);
    test_assert( ecs_has_pair(world, child, EcsChildOf, parent_2));
    test_assert( !ecs_has_pair(world, child, EcsChildOf, parent_1));

    ecs_delete(world, parent_1);

    test_assert( ecs_is_alive(world, child));

    ecs_fini(world);
}

void Hierarchies_lookup_after_root_to_parent_move(void) {
    ecs_world_t *world = ecs_mini();

    ecs_entity_t p = ecs_new_entity(world, "Parent");
    ecs_entity_t c = ecs_new_entity(world, "Child");

    test_str("Parent", ecs_get_name(world, p));
    test_str("Child", ecs_get_name(world, c));

    test_uint(p, ecs_lookup_fullpath(world, "Parent"));
    test_uint(c, ecs_lookup_fullpath(world, "Child"));

    char *path = ecs_get_fullpath(world, p);
    test_str(path, "Parent");
    ecs_os_free(path);

    path = ecs_get_fullpath(world, c);
    test_str(path, "Child");
    ecs_os_free(path);

    ecs_add_pair(world, c, EcsChildOf, p);
    
    test_str("Child", ecs_get_name(world, c));
    test_uint(0, ecs_lookup_fullpath(world, "Child"));
    test_uint(c, ecs_lookup_fullpath(world, "Parent.Child"));

    path = ecs_get_fullpath(world, c);
    test_str(path, "Parent.Child");
    ecs_os_free(path);

    ecs_fini(world);
}

void Hierarchies_lookup_after_parent_to_root_move(void) {
    ecs_world_t *world = ecs_mini();

    ecs_entity_t p = ecs_new_entity(world, "Parent");
    ecs_entity_t c = ecs_new_entity(world, "Parent.Child");

    test_str("Parent", ecs_get_name(world, p));
    test_str("Child", ecs_get_name(world, c));

    test_uint(p, ecs_lookup_fullpath(world, "Parent"));
    test_uint(0, ecs_lookup_fullpath(world, "Child"));
    test_uint(c, ecs_lookup_fullpath(world, "Parent.Child"));

    char *path = ecs_get_fullpath(world, p);
    test_str(path, "Parent");
    ecs_os_free(path);

    path = ecs_get_fullpath(world, c);
    test_str(path, "Parent.Child");
    ecs_os_free(path);

    ecs_remove_pair(world, c, EcsChildOf, p);
    
    test_str("Child", ecs_get_name(world, c));
    test_uint(c, ecs_lookup_fullpath(world, "Child"));
    test_uint(0, ecs_lookup_fullpath(world, "Parent.Child"));

    path = ecs_get_fullpath(world, c);
    test_str(path, "Child");
    ecs_os_free(path);

    ecs_fini(world);
}

void Hierarchies_lookup_after_parent_to_parent_move(void) {
    ecs_world_t *world = ecs_mini();

    ecs_entity_t p = ecs_new_entity(world, "Parent");
    ecs_entity_t p2 = ecs_new_entity(world, "Parent2");
    ecs_entity_t c = ecs_new_entity(world, "Parent.Child");

    test_str("Parent", ecs_get_name(world, p));
    test_str("Parent2", ecs_get_name(world, p2));
    test_str("Child", ecs_get_name(world, c));

    test_uint(p, ecs_lookup_fullpath(world, "Parent"));
    test_uint(0, ecs_lookup_fullpath(world, "Child"));
    test_uint(c, ecs_lookup_fullpath(world, "Parent.Child"));
    test_uint(0, ecs_lookup_fullpath(world, "Parent2.Child"));

    char *path = ecs_get_fullpath(world, p);
    test_str(path, "Parent");
    ecs_os_free(path);

    path = ecs_get_fullpath(world, c);
    test_str(path, "Parent.Child");
    ecs_os_free(path);

    ecs_add_pair(world, c, EcsChildOf, p2);
    
    test_str("Child", ecs_get_name(world, c));
    test_uint(0, ecs_lookup_fullpath(world, "Child"));
    test_uint(0, ecs_lookup_fullpath(world, "Parent.Child"));
    test_uint(c, ecs_lookup_fullpath(world, "Parent2.Child"));

    path = ecs_get_fullpath(world, c);
    test_str(path, "Parent2.Child");
    ecs_os_free(path);

    ecs_fini(world);
}

void Hierarchies_lookup_after_clear_from_root(void) {
    ecs_world_t *world = ecs_mini();

    ecs_entity_t c = ecs_new_entity(world, "Child");

    test_str("Child", ecs_get_name(world, c));

    test_uint(c, ecs_lookup_fullpath(world, "Child"));

    char *path = ecs_get_fullpath(world, c);
    test_str(path, "Child");
    ecs_os_free(path);

    ecs_clear(world, c);
    
    test_str(NULL, ecs_get_name(world, c));
    test_uint(0, ecs_lookup_fullpath(world, "Child"));

    ecs_fini(world);
}

void Hierarchies_lookup_after_clear_from_parent(void) {
    ecs_world_t *world = ecs_mini();

    ecs_entity_t p = ecs_new_entity(world, "Parent");
    ecs_entity_t c = ecs_new_entity(world, "Parent.Child");

    test_str("Parent", ecs_get_name(world, p));
    test_str("Child", ecs_get_name(world, c));

    test_uint(p, ecs_lookup_fullpath(world, "Parent"));
    test_uint(0, ecs_lookup_fullpath(world, "Child"));
    test_uint(c, ecs_lookup_fullpath(world, "Parent.Child"));

    char *path = ecs_get_fullpath(world, p);
    test_str(path, "Parent");
    ecs_os_free(path);

    path = ecs_get_fullpath(world, c);
    test_str(path, "Parent.Child");
    ecs_os_free(path);

    ecs_clear(world, c);
    
    test_str(NULL, ecs_get_name(world, c));
    test_uint(0, ecs_lookup_fullpath(world, "Child"));
    test_uint(0, ecs_lookup_fullpath(world, "Parent.Child"));

    ecs_fini(world);
}

void Hierarchies_lookup_after_delete_from_root(void) {
    ecs_world_t *world = ecs_mini();

    ecs_entity_t c = ecs_new_entity(world, "Child");

    test_str("Child", ecs_get_name(world, c));

    test_uint(c, ecs_lookup_fullpath(world, "Child"));

    char *path = ecs_get_fullpath(world, c);
    test_str(path, "Child");
    ecs_os_free(path);

    ecs_delete(world, c);
    
    test_uint(0, ecs_lookup_fullpath(world, "Child"));

    ecs_fini(world);
}

void Hierarchies_lookup_after_delete_from_parent(void) {
    ecs_world_t *world = ecs_mini();

    ecs_entity_t p = ecs_new_entity(world, "Parent");
    ecs_entity_t c = ecs_new_entity(world, "Parent.Child");

    test_str("Parent", ecs_get_name(world, p));
    test_str("Child", ecs_get_name(world, c));

    test_uint(p, ecs_lookup_fullpath(world, "Parent"));
    test_uint(0, ecs_lookup_fullpath(world, "Child"));
    test_uint(c, ecs_lookup_fullpath(world, "Parent.Child"));

    char *path = ecs_get_fullpath(world, p);
    test_str(path, "Parent");
    ecs_os_free(path);

    path = ecs_get_fullpath(world, c);
    test_str(path, "Parent.Child");
    ecs_os_free(path);

    ecs_delete(world, c);

    test_uint(0, ecs_lookup_fullpath(world, "Child"));
    test_uint(0, ecs_lookup_fullpath(world, "Parent.Child"));

    ecs_fini(world);
}

void Hierarchies_defer_batch_remove_name_w_add_childof(void) {
    ecs_world_t *world = ecs_mini();

    ecs_entity_t e = ecs_new_entity(world, "e");
    test_assert(e != 0);
    test_str("e", ecs_get_name(world, e));
    test_assert(ecs_lookup_fullpath(world, "e") == e);

    ecs_entity_t parent = ecs_new_id(world);
    ecs_defer_begin(world);
    ecs_clear(world, e);
    ecs_add_pair(world, e, EcsChildOf, parent);
    ecs_defer_end(world);

    test_assert(ecs_lookup_fullpath(world, "e") == 0);
    test_assert(ecs_has_pair(world, e, EcsChildOf, parent));
    test_assert(ecs_get_name(world, e) == NULL);

    ecs_fini(world);
}

void Hierarchies_defer_batch_remove_childof_w_add_name(void) {
    ecs_world_t *world = ecs_mini();

    ecs_entity_t parent = ecs_new_id(world);
    ecs_entity_t e = ecs_new_w_pair(world, EcsChildOf, parent);
    test_assert(e != 0);
    
    ecs_defer_begin(world);
    ecs_clear(world, e);
    ecs_set_name(world, e, "e");
    ecs_defer_end(world);

    test_assert(ecs_get_name(world, e) != NULL);
    test_assert(ecs_lookup_fullpath(world, "e") == e);
    test_assert(!ecs_has_pair(world, e, EcsChildOf, parent));

    ecs_fini(world);
}
