flecs_ecs 0.2.2

Rust API for the C/CPP flecs ECS library <https://github.com/SanderMertens/flecs>
Documentation
#![allow(dead_code)]
use flecs_ecs::core::*;
use flecs_ecs::macros::*;

use crate::common_test::*;

#[test]
fn query_uncached_destruction_no_panic() {
    let world = World::new();
    let query = world.new_query::<&Tag>();
    let query2 = query.clone();
    drop(query);
    query2.run(|mut it| while it.next() {});
    drop(query2);
}

#[test]
#[should_panic]
fn query_cached_destruction_lingering_references_panic() {
    let world = World::new();
    let query = world.query::<&Tag>().set_cached().build();
    let query2 = query.clone();
    query.destruct();
    query2.run(|_| {});
    drop(query2);
}

#[test]
fn query_iter_stage() {
    #[derive(Component, Debug)]
    struct Comp(usize);

    let world = World::new();
    world.set_threads(4);

    let query = world.new_query::<&Comp>();

    for i in 0..4 {
        world.entity().set(Comp(i));
    }

    world.system::<&Comp>().par_each_entity(move |e, _| {
        query.iter_stage(e).each(|_vel| {});
    });

    world.progress();
}

#[test]
#[should_panic]
fn query_panic_inside() {
    let world = World::new();
    let query = world.query::<&Tag>().build();
    query.run(|_| {
        panic!();
    });
}

#[test]
fn query_run_sparse() {
    let world = World::new();

    world.component::<Position>().add_trait::<flecs::Sparse>();
    world.component::<Velocity>();

    let entity = world
        .entity()
        .set(Position { x: 10, y: 20 })
        .set(Velocity { x: 1, y: 2 });

    let q = world.query::<(&mut Position, &Velocity)>().build();

    q.run(|mut it| {
        while it.next() {
            let v = it.field::<Velocity>(1);

            for i in it.iter() {
                let mut p = it.field_at_mut::<Position>(0, i);
                p.x += v[i].x;
                p.y += v[i].y;
            }
        }
    });

    entity.get::<&Position>(|p| {
        assert_eq!(p.x, 11);
        assert_eq!(p.y, 22);
    });
}

#[test]
fn query_each_sparse() {
    let world = World::new();

    world.component::<Position>().add_trait::<flecs::Sparse>();
    world.component::<Velocity>();

    let entity = world
        .entity()
        .set(Position { x: 10, y: 20 })
        .set(Velocity { x: 1, y: 2 });

    let q = world.query::<(&mut Position, &Velocity)>().build();

    q.each(|(p, v)| {
        p.x += v.x;
        p.y += v.y;
    });

    entity.get::<&Position>(|p| {
        assert_eq!(p.x, 11);
        assert_eq!(p.y, 22);
    });
}

#[test]
fn query_each_sparse_many() {
    let world = World::new();

    world.component::<Position>().add_trait::<flecs::Sparse>();
    world.component::<Velocity>();

    let mut entities = Vec::new();

    for i in 0..2000 {
        entities.push(
            world
                .entity_named(i.to_string().as_str())
                .set(Position {
                    x: 10 + i,
                    y: 20 + i,
                })
                .set(Velocity { x: i, y: i })
                .id(),
        );
    }

    let q = world.query::<(&mut Position, &Velocity)>().build();

    q.each(|(p, v)| {
        p.x += v.x;
        p.y += v.y;
    });

    for i in 0..2000_i32 {
        let e = world.entity_from_id(entities[i as usize]);
        e.get::<&Position>(|p| {
            assert_eq!(p.x, 10 + i * 2);
            assert_eq!(p.y, 20 + i * 2);
        });
        e.get::<&Velocity>(|v| {
            assert_eq!(v.x, i);
            assert_eq!(v.y, i);
        });
    }
}

#[test]
fn query_iter_targets() {
    let world = World::new();

    let likes = world.entity();
    let pizza = world.entity();
    let salad = world.entity();
    let alice = world.entity().add((likes, pizza)).add((likes, salad));

    let q = world
        .query::<()>()
        .with((likes, id::<flecs::Any>()))
        .build();

    let mut count = 0;
    let mut tgt_count = 0;

    q.run(|mut it| {
        while it.next() {
            for i in it.iter() {
                let e = it.get_entity(i).unwrap();
                assert_eq!(e, alice);

                it.targets(0, |tgt| {
                    if tgt_count == 0 {
                        assert_eq!(tgt, pizza);
                    }
                    if tgt_count == 1 {
                        assert_eq!(tgt, salad);
                    }
                    tgt_count += 1;
                });

                count += 1;
            }
        }
    });

    assert_eq!(count, 1);
    assert_eq!(tgt_count, 2);
}

#[test]
fn query_iter_targets_second_field() {
    let world = World::new();

    let likes = world.entity();
    let pizza = world.entity();
    let salad = world.entity();
    let alice = world
        .entity()
        .add(Position::id())
        .add((likes, pizza))
        .add((likes, salad));

    let q = world
        .query::<&Position>()
        .with((likes, id::<flecs::Any>()))
        .build();

    let mut count = 0;
    let mut tgt_count = 0;

    q.run(|mut it| {
        while it.next() {
            for i in it.iter() {
                let e = it.get_entity(i).unwrap();
                assert_eq!(e, alice);

                it.targets(1, |tgt| {
                    if tgt_count == 0 {
                        assert_eq!(tgt, pizza);
                    }
                    if tgt_count == 1 {
                        assert_eq!(tgt, salad);
                    }
                    tgt_count += 1;
                });
                count += 1;
            }
        }
    });

    assert_eq!(count, 1);
    assert_eq!(tgt_count, 2);
}

#[test]
#[should_panic]
#[cfg(debug_assertions)]
fn query_iter_targets_field_out_of_range() {
    let world = World::new();

    let likes = world.entity();
    let pizza = world.entity();
    let salad = world.entity();
    let alice = world.entity().add((likes, pizza)).add((likes, salad));

    let q = world
        .query::<()>()
        .with((likes, id::<flecs::Any>()))
        .build();

    q.run(|mut it| {
        while it.next() {
            for i in it.iter() {
                let e = it.get_entity(i).unwrap();
                assert_eq!(e, alice);

                // This should panic because the index 1 is out of range
                it.targets(1, |_| {});
            }
        }
    });
}

#[test]
#[should_panic]
#[cfg(debug_assertions)]
fn query_iter_targets_field_not_a_pair() {
    let world = World::new();

    let likes = world.entity();
    let pizza = world.entity();
    let salad = world.entity();
    let alice = world
        .entity()
        .add(Position::id())
        .add((likes, pizza))
        .add((likes, salad));

    let q = world.query::<&Position>().build();

    q.run(|mut it| {
        while it.next() {
            for i in it.iter() {
                let e = it.get_entity(i).unwrap();
                assert_eq!(e, alice);

                it.targets(1, |_| {});
            }
        }
    });
}

#[test]
#[should_panic]
#[cfg(debug_assertions)]
fn query_iter_targets_field_not_set() {
    let world = World::new();

    let likes = world.entity();
    let alice = world.entity().add(Position::id());

    let q = world
        .query::<&Position>()
        .with((likes, id::<flecs::Any>()))
        .optional()
        .build();

    q.run(|mut it| {
        while it.next() {
            for i in it.iter() {
                let e = it.get_entity(i).unwrap();
                assert_eq!(e, alice);

                it.targets(1, |_| {});
            }
        }
    });
}