#include <addons.h>

void Metrics_member_gauge_1_entity(void) {
    ecs_world_t *world = ecs_init();
    ECS_IMPORT(world, FlecsMetrics);

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            { "x", ecs_id(ecs_f32_t) },
            { "y", ecs_id(ecs_f32_t) },
        }
    });

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.position_y" }),
        .member = ecs_lookup_fullpath(world, "Position.y"),
        .kind = EcsGauge
    });
    test_assert(m != 0);

    ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20});

    ecs_progress(world, 0);
    
    ecs_iter_t it = ecs_children(world, m);
    test_bool(true, ecs_children_next(&it));
    test_uint(it.count, 1);
    {
        ecs_entity_t e = it.entities[0];
        test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
        const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
        const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
        test_assert(src != NULL);
        test_assert(inst != NULL);
        test_uint(src->entity, e1);
        test_int(inst->value, 20);
    }

    test_bool(false, ecs_children_next(&it));

    ecs_fini(world);
}

void Metrics_member_gauge_2_entities(void) {
    ecs_world_t *world = ecs_init();
    ECS_IMPORT(world, FlecsMetrics);

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            { "x", ecs_id(ecs_f32_t) },
            { "y", ecs_id(ecs_f32_t) },
        }
    });

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.position_y" }),
        .member = ecs_lookup_fullpath(world, "Position.y"),
        .kind = EcsGauge
    });
    test_assert(m != 0);

    ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20});
    ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30});

    ecs_progress(world, 0);
    
    ecs_iter_t it = ecs_children(world, m);
    test_bool(true, ecs_children_next(&it));
    test_uint(it.count, 2);
    {
        ecs_entity_t e = it.entities[0];
        test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
        const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
        const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
        test_assert(src != NULL);
        test_assert(inst != NULL);
        test_uint(src->entity, e1);
        test_int(inst->value, 20);
    }
    {
        ecs_entity_t e = it.entities[1];
        test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
        const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
        const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
        test_assert(src != NULL);
        test_assert(inst != NULL);
        test_uint(src->entity, e2);
        test_int(inst->value, 30);
    }

    test_bool(false, ecs_children_next(&it));

    ecs_fini(world);
}

void Metrics_member_gauge_2_entities_1_existing(void) {
    ecs_world_t *world = ecs_init();
    ECS_IMPORT(world, FlecsMetrics);

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            { "x", ecs_id(ecs_f32_t) },
            { "y", ecs_id(ecs_f32_t) },
        }
    });

    ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20});

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.position_y" }),
        .member = ecs_lookup_fullpath(world, "Position.y"),
        .kind = EcsGauge
    });
    test_assert(m != 0);

    ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30});

    ecs_progress(world, 0);
    
    ecs_iter_t it = ecs_children(world, m);
    test_bool(true, ecs_children_next(&it));
    test_uint(it.count, 2);
    {
        ecs_entity_t e = it.entities[0];
        test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
        const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
        const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
        test_assert(src != NULL);
        test_assert(inst != NULL);
        test_uint(src->entity, e1);
        test_int(inst->value, 20);
    }
    {
        ecs_entity_t e = it.entities[1];
        test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
        const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
        const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
        test_assert(src != NULL);
        test_assert(inst != NULL);
        test_uint(src->entity, e2);
        test_int(inst->value, 30);
    }

    test_bool(false, ecs_children_next(&it));

    ecs_fini(world);
}

void Metrics_member_gauge_2_entities_update(void) {
    ecs_world_t *world = ecs_init();
    ECS_IMPORT(world, FlecsMetrics);

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            { "x", ecs_id(ecs_f32_t) },
            { "y", ecs_id(ecs_f32_t) },
        }
    });

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.position_y" }),
        .member = ecs_lookup_fullpath(world, "Position.y"),
        .kind = EcsGauge
    });
    test_assert(m != 0);

    ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20});
    ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30});

    ecs_progress(world, 0);
    
    {
        ecs_iter_t it = ecs_children(world, m);
        test_bool(true, ecs_children_next(&it));
        test_uint(it.count, 2);
        {
            ecs_entity_t e = it.entities[0];
            test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e1);
            test_int(inst->value, 20);
        }
        {
            ecs_entity_t e = it.entities[1];
            test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e2);
            test_int(inst->value, 30);
        }
        test_bool(false, ecs_children_next(&it));
    }

    ecs_set(world, e1, Position, {15, 25});
    ecs_set(world, e2, Position, {25, 35});

    ecs_progress(world, 0);

    {
        ecs_iter_t it = ecs_children(world, m);
        test_bool(true, ecs_children_next(&it));
        test_uint(it.count, 2);
        {
            ecs_entity_t e = it.entities[0];
            test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e1);
            test_int(inst->value, 25);
        }
        {
            ecs_entity_t e = it.entities[1];
            test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e2);
            test_int(inst->value, 35);
        }
        test_bool(false, ecs_children_next(&it));
    }

    ecs_fini(world);
}

void Metrics_member_gauge_w_remove(void) {
    ecs_world_t *world = ecs_init();
    ECS_IMPORT(world, FlecsMetrics);

    ECS_COMPONENT(world, Position);
    ECS_TAG(world, Foo);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            { "x", ecs_id(ecs_f32_t) },
            { "y", ecs_id(ecs_f32_t) },
        }
    });

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.position_y" }),
        .member = ecs_lookup_fullpath(world, "Position.y"),
        .kind = EcsGauge
    });
    test_assert(m != 0);

    ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20});
    ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30});
    ecs_add(world, e1, Foo);
    ecs_add(world, e2, Foo);

    ecs_progress(world, 0);
    
    {
        ecs_iter_t it = ecs_children(world, m);
        test_bool(true, ecs_children_next(&it));
        test_uint(it.count, 2);
        {
            ecs_entity_t e = it.entities[0];
            test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e1);
            test_int(inst->value, 20);
        }
        {
            ecs_entity_t e = it.entities[1];
            test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e2);
            test_int(inst->value, 30);
        }
        test_bool(false, ecs_children_next(&it));
    }

    ecs_remove(world, e1, Position);
    ecs_progress(world, 0);

    {
        ecs_iter_t it = ecs_children(world, m);
        test_bool(true, ecs_children_next(&it));
        test_uint(it.count, 1);
        {
            ecs_entity_t e = it.entities[0];
            test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e2);
            test_int(inst->value, 30);
        }
        test_bool(false, ecs_children_next(&it));
    }

    ecs_fini(world);
}

void Metrics_member_gauge_w_clear(void) {
    ecs_world_t *world = ecs_init();
    ECS_IMPORT(world, FlecsMetrics);

    ECS_COMPONENT(world, Position);
    ECS_TAG(world, Foo);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            { "x", ecs_id(ecs_f32_t) },
            { "y", ecs_id(ecs_f32_t) },
        }
    });

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.position_y" }),
        .member = ecs_lookup_fullpath(world, "Position.y"),
        .kind = EcsGauge
    });
    test_assert(m != 0);

    ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20});
    ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30});
    ecs_add(world, e1, Foo);
    ecs_add(world, e2, Foo);

    ecs_progress(world, 0);
    
    {
        ecs_iter_t it = ecs_children(world, m);
        test_bool(true, ecs_children_next(&it));
        test_uint(it.count, 2);
        {
            ecs_entity_t e = it.entities[0];
            test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e1);
            test_int(inst->value, 20);
        }
        {
            ecs_entity_t e = it.entities[1];
            test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e2);
            test_int(inst->value, 30);
        }
        test_bool(false, ecs_children_next(&it));
    }

    ecs_clear(world, e1);
    ecs_progress(world, 0);

    {
        ecs_iter_t it = ecs_children(world, m);
        test_bool(true, ecs_children_next(&it));
        test_uint(it.count, 1);
        {
            ecs_entity_t e = it.entities[0];
            test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e2);
            test_int(inst->value, 30);
        }
        test_bool(false, ecs_children_next(&it));
    }

    ecs_fini(world);
}

void Metrics_member_gauge_w_delete(void) {
    ecs_world_t *world = ecs_init();
    ECS_IMPORT(world, FlecsMetrics);

    ECS_COMPONENT(world, Position);
    ECS_TAG(world, Foo);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            { "x", ecs_id(ecs_f32_t) },
            { "y", ecs_id(ecs_f32_t) },
        }
    });

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.position_y" }),
        .member = ecs_lookup_fullpath(world, "Position.y"),
        .kind = EcsGauge
    });
    test_assert(m != 0);

    ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20});
    ecs_entity_t e2 = ecs_set(world, 0, Position, {20, 30});
    ecs_add(world, e1, Foo);
    ecs_add(world, e2, Foo);

    ecs_progress(world, 0);
    
    {
        ecs_iter_t it = ecs_children(world, m);
        test_bool(true, ecs_children_next(&it));
        test_uint(it.count, 2);
        {
            ecs_entity_t e = it.entities[0];
            test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e1);
            test_int(inst->value, 20);
        }
        {
            ecs_entity_t e = it.entities[1];
            test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e2);
            test_int(inst->value, 30);
        }
        test_bool(false, ecs_children_next(&it));
    }

    ecs_delete(world, e1);
    ecs_progress(world, 0);

    {
        ecs_iter_t it = ecs_children(world, m);
        test_bool(true, ecs_children_next(&it));
        test_uint(it.count, 1);
        {
            ecs_entity_t e = it.entities[0];
            test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e2);
            test_int(inst->value, 30);
        }
        test_bool(false, ecs_children_next(&it));
    }

    ecs_fini(world);
}

void Metrics_id_gauge_1_entity(void) {
    ecs_world_t *world = ecs_init();
    ECS_IMPORT(world, FlecsMetrics);

    ECS_TAG(world, Foo);

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.has_foo" }),
        .id = Foo,
        .kind = EcsGauge
    });
    test_assert(m != 0);

    ecs_entity_t e1 = ecs_new(world, Foo);

    ecs_progress(world, 0);
    
    ecs_iter_t it = ecs_children(world, m);
    test_bool(true, ecs_children_next(&it));
    test_uint(it.count, 1);
    {
        ecs_entity_t e = it.entities[0];
        test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
        const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
        const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
        test_assert(src != NULL);
        test_assert(inst != NULL);
        test_uint(src->entity, e1);
        test_int(inst->value, 1);
    }

    test_bool(false, ecs_children_next(&it));

    ecs_fini(world);
}

void Metrics_id_gauge_2_entities(void) {
    ecs_world_t *world = ecs_init();
    ECS_IMPORT(world, FlecsMetrics);

    ECS_TAG(world, Foo);

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.has_foo" }),
        .id = Foo,
        .kind = EcsGauge
    });
    test_assert(m != 0);

    ecs_entity_t e1 = ecs_new(world, Foo);
    ecs_entity_t e2 = ecs_new(world, Foo);

    ecs_progress(world, 0);
    
    ecs_iter_t it = ecs_children(world, m);
    test_bool(true, ecs_children_next(&it));
    test_uint(it.count, 2);
    {
        ecs_entity_t e = it.entities[0];
        test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
        const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
        const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
        test_assert(src != NULL);
        test_assert(inst != NULL);
        test_uint(src->entity, e1);
        test_int(inst->value, 1);
    }
    {
        ecs_entity_t e = it.entities[1];
        test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
        const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
        const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
        test_assert(src != NULL);
        test_assert(inst != NULL);
        test_uint(src->entity, e2);
        test_int(inst->value, 1);
    }

    test_bool(false, ecs_children_next(&it));

    ecs_fini(world);
}

void Metrics_id_gauge_2_entities_1_existing(void) {
    ecs_world_t *world = ecs_init();
    ECS_IMPORT(world, FlecsMetrics);

    ECS_TAG(world, Foo);

    ecs_entity_t e1 = ecs_new(world, Foo);

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.has_foo" }),
        .id = Foo,
        .kind = EcsGauge
    });
    test_assert(m != 0);

    ecs_entity_t e2 = ecs_new(world, Foo);

    ecs_progress(world, 0);
    
    ecs_iter_t it = ecs_children(world, m);
    test_bool(true, ecs_children_next(&it));
    test_uint(it.count, 2);
    {
        ecs_entity_t e = it.entities[0];
        test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
        const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
        const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
        test_assert(src != NULL);
        test_assert(inst != NULL);
        test_uint(src->entity, e1);
        test_int(inst->value, 1);
    }
    {
        ecs_entity_t e = it.entities[1];
        test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
        const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
        const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
        test_assert(src != NULL);
        test_assert(inst != NULL);
        test_uint(src->entity, e2);
        test_int(inst->value, 1);
    }

    test_bool(false, ecs_children_next(&it));

    ecs_fini(world);
}

void Metrics_id_gauge_w_remove(void) {
    ecs_world_t *world = ecs_init();
    ECS_IMPORT(world, FlecsMetrics);

    ECS_TAG(world, Foo);
    ECS_TAG(world, Bar);

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.has_foo" }),
        .id = Foo,
        .kind = EcsGauge
    });
    test_assert(m != 0);

    ecs_entity_t e1 = ecs_new(world, Foo);
    ecs_entity_t e2 = ecs_new(world, Foo);
    ecs_add(world, e1, Bar);
    ecs_add(world, e2, Bar);

    ecs_progress(world, 0);

    {    
        ecs_iter_t it = ecs_children(world, m);
        test_bool(true, ecs_children_next(&it));
        test_uint(it.count, 2);
        {
            ecs_entity_t e = it.entities[0];
            test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e1);
            test_int(inst->value, 1);
        }
        {
            ecs_entity_t e = it.entities[1];
            test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e2);
            test_int(inst->value, 1);
        }
        test_bool(false, ecs_children_next(&it));
    }

    ecs_remove(world, e1, Foo);
    ecs_progress(world, 0);

    {    
        ecs_iter_t it = ecs_children(world, m);
        test_bool(true, ecs_children_next(&it));
        test_uint(it.count, 1);
        {
            ecs_entity_t e = it.entities[0];
            test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e2);
            test_int(inst->value, 1);
        }
        test_bool(false, ecs_children_next(&it));
    }

    ecs_fini(world);
}

void Metrics_id_gauge_w_clear(void) {
    ecs_world_t *world = ecs_init();
    ECS_IMPORT(world, FlecsMetrics);

    ECS_TAG(world, Foo);
    ECS_TAG(world, Bar);

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.has_foo" }),
        .id = Foo,
        .kind = EcsGauge
    });
    test_assert(m != 0);

    ecs_entity_t e1 = ecs_new(world, Foo);
    ecs_entity_t e2 = ecs_new(world, Foo);
    ecs_add(world, e1, Bar);
    ecs_add(world, e2, Bar);

    ecs_progress(world, 0);

    {    
        ecs_iter_t it = ecs_children(world, m);
        test_bool(true, ecs_children_next(&it));
        test_uint(it.count, 2);
        {
            ecs_entity_t e = it.entities[0];
            test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e1);
            test_int(inst->value, 1);
        }
        {
            ecs_entity_t e = it.entities[1];
            test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e2);
            test_int(inst->value, 1);
        }
        test_bool(false, ecs_children_next(&it));
    }

    ecs_clear(world, e1);
    ecs_progress(world, 0);

    {    
        ecs_iter_t it = ecs_children(world, m);
        test_bool(true, ecs_children_next(&it));
        test_uint(it.count, 1);
        {
            ecs_entity_t e = it.entities[0];
            test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e2);
            test_int(inst->value, 1);
        }
        test_bool(false, ecs_children_next(&it));
    }

    ecs_fini(world);
}

void Metrics_id_gauge_w_delete(void) {
    ecs_world_t *world = ecs_init();
    ECS_IMPORT(world, FlecsMetrics);

    ECS_TAG(world, Foo);
    ECS_TAG(world, Bar);

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.has_foo" }),
        .id = Foo,
        .kind = EcsGauge
    });
    test_assert(m != 0);

    ecs_entity_t e1 = ecs_new(world, Foo);
    ecs_entity_t e2 = ecs_new(world, Foo);
    ecs_add(world, e1, Bar);
    ecs_add(world, e2, Bar);

    ecs_progress(world, 0);

    {    
        ecs_iter_t it = ecs_children(world, m);
        test_bool(true, ecs_children_next(&it));
        test_uint(it.count, 2);
        {
            ecs_entity_t e = it.entities[0];
            test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e1);
            test_int(inst->value, 1);
        }
        {
            ecs_entity_t e = it.entities[1];
            test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e2);
            test_int(inst->value, 1);
        }
        test_bool(false, ecs_children_next(&it));
    }

    ecs_delete(world, e1);
    ecs_progress(world, 0);

    {    
        ecs_iter_t it = ecs_children(world, m);
        test_bool(true, ecs_children_next(&it));
        test_uint(it.count, 1);
        {
            ecs_entity_t e = it.entities[0];
            test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e2);
            test_int(inst->value, 1);
        }
        test_bool(false, ecs_children_next(&it));
    }

    ecs_fini(world);
}

void Metrics_oneof_gauge_3_entities(void) {
    ecs_world_t *world = ecs_init();
    ECS_IMPORT(world, FlecsMetrics);

    ECS_ENTITY(world, Color, OneOf, Exclusive);
    ECS_ENTITY(world, Red,   (ChildOf, Color));
    ECS_ENTITY(world, Green, (ChildOf, Color));
    ECS_ENTITY(world, Blue,  (ChildOf, Color));

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.color" }),
        .id = ecs_pair(Color, EcsWildcard),
        .targets = true,
        .kind = EcsGauge
    });
    test_assert(m != 0);

    ecs_entity_t e1 = ecs_new_w_pair(world, Color, Red);
    ecs_entity_t e2 = ecs_new_w_pair(world, Color, Green);
    ecs_entity_t e3 = ecs_new_w_pair(world, Color, Blue);

    ecs_progress(world, 0);

    ecs_filter_t *f = ecs_filter(world, {
        .terms = {
            { .id = ecs_childof(m) },
            { .id = ecs_pair(EcsMetric, EcsGauge) }
        }
    });
    test_assert(f != NULL);

    ecs_iter_t it = ecs_filter_iter(world, f);
    test_bool(true, ecs_filter_next(&it));
    test_uint(it.count, 3);
    {
        ecs_entity_t e = it.entities[0];
        const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
        const void *inst = ecs_get_id(world, e, ecs_pair(m, ecs_id(EcsMetricValue)));
        test_assert(src != NULL);
        test_assert(inst != NULL);
        test_uint(src->entity, e1);
        test_int(((double*)inst)[0], 1);
        test_int(((double*)inst)[1], 0);
        test_int(((double*)inst)[2], 0);
    }
    {
        ecs_entity_t e = it.entities[1];
        const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
        const void *inst = ecs_get_id(world, e, ecs_pair(m, ecs_id(EcsMetricValue)));
        test_assert(src != NULL);
        test_assert(inst != NULL);
        test_uint(src->entity, e2);
        test_int(((double*)inst)[0], 0);
        test_int(((double*)inst)[1], 1);
        test_int(((double*)inst)[2], 0);
    }
    {
        ecs_entity_t e = it.entities[2];
        const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
        const void *inst = ecs_get_id(world, e, ecs_pair(m, ecs_id(EcsMetricValue)));
        test_assert(src != NULL);
        test_assert(inst != NULL);
        test_uint(src->entity, e3);
        test_int(((double*)inst)[0], 0);
        test_int(((double*)inst)[1], 0);
        test_int(((double*)inst)[2], 1);
    }
    test_bool(false, ecs_filter_next(&it));

    ecs_filter_fini(f);

    ecs_fini(world);
}

void Metrics_oneof_gauge_3_entities_1_existing(void) {
    ecs_world_t *world = ecs_init();
    ECS_IMPORT(world, FlecsMetrics);

    ECS_ENTITY(world, Color, OneOf, Exclusive);
    ECS_ENTITY(world, Red,   (ChildOf, Color));
    ECS_ENTITY(world, Green, (ChildOf, Color));
    ECS_ENTITY(world, Blue,  (ChildOf, Color));

    ecs_entity_t e1 = ecs_new_w_pair(world, Color, Red);

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.color" }),
        .id = ecs_pair(Color, EcsWildcard),
        .targets = true,
        .kind = EcsGauge
    });
    test_assert(m != 0);

    ecs_entity_t e2 = ecs_new_w_pair(world, Color, Green);
    ecs_entity_t e3 = ecs_new_w_pair(world, Color, Blue);

    ecs_progress(world, 0);

    ecs_filter_t *f = ecs_filter(world, {
        .terms = {
            { .id = ecs_childof(m) },
            { .id = ecs_pair(EcsMetric, EcsGauge) }
        }
    });
    test_assert(f != NULL);

    ecs_iter_t it = ecs_filter_iter(world, f);
    test_bool(true, ecs_filter_next(&it));
    test_uint(it.count, 3);
    {
        ecs_entity_t e = it.entities[0];
        const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
        const void *inst = ecs_get_id(world, e, ecs_pair(m, ecs_id(EcsMetricValue)));
        test_assert(src != NULL);
        test_assert(inst != NULL);
        test_uint(src->entity, e1);
        test_int(((double*)inst)[0], 1);
        test_int(((double*)inst)[1], 0);
        test_int(((double*)inst)[2], 0);
    }
    {
        ecs_entity_t e = it.entities[1];
        const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
        const void *inst = ecs_get_id(world, e, ecs_pair(m, ecs_id(EcsMetricValue)));
        test_assert(src != NULL);
        test_assert(inst != NULL);
        test_uint(src->entity, e2);
        test_int(((double*)inst)[0], 0);
        test_int(((double*)inst)[1], 1);
        test_int(((double*)inst)[2], 0);
    }
    {
        ecs_entity_t e = it.entities[2];
        const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
        const void *inst = ecs_get_id(world, e, ecs_pair(m, ecs_id(EcsMetricValue)));
        test_assert(src != NULL);
        test_assert(inst != NULL);
        test_uint(src->entity, e3);
        test_int(((double*)inst)[0], 0);
        test_int(((double*)inst)[1], 0);
        test_int(((double*)inst)[2], 1);
    }
    test_bool(false, ecs_filter_next(&it));

    ecs_filter_fini(f);

    ecs_fini(world);
}

void Metrics_oneof_gauge_w_remove(void) {
    ecs_world_t *world = ecs_init();
    ECS_IMPORT(world, FlecsMetrics);

    ECS_ENTITY(world, Color, OneOf, Exclusive);
    ECS_ENTITY(world, Red,   (ChildOf, Color));
    ECS_ENTITY(world, Green, (ChildOf, Color));
    ECS_ENTITY(world, Blue,  (ChildOf, Color));
    ECS_TAG(world, Foo);

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.color" }),
        .id = ecs_pair(Color, EcsWildcard),
        .targets = true,
        .kind = EcsGauge
    });
    test_assert(m != 0);

    ecs_entity_t e1 = ecs_new_w_pair(world, Color, Red);
    ecs_entity_t e2 = ecs_new_w_pair(world, Color, Green);
    ecs_entity_t e3 = ecs_new_w_pair(world, Color, Blue);
    ecs_add(world, e1, Foo);
    ecs_add(world, e2, Foo);
    ecs_add(world, e3, Foo);
    
    ecs_progress(world, 0);

    ecs_filter_t *f = ecs_filter(world, {
        .terms = {
            { .id = ecs_childof(m) },
            { .id = ecs_pair(EcsMetric, EcsGauge) }
        }
    });
    test_assert(f != NULL);

    {
        ecs_iter_t it = ecs_filter_iter(world, f);
        test_bool(true, ecs_filter_next(&it));
        test_uint(it.count, 3);
        {
            ecs_entity_t e = it.entities[0];
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const void *inst = ecs_get_id(world, e, ecs_pair(m, ecs_id(EcsMetricValue)));
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e1);
            test_int(((double*)inst)[0], 1);
            test_int(((double*)inst)[1], 0);
            test_int(((double*)inst)[2], 0);
        }
        {
            ecs_entity_t e = it.entities[1];
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const void *inst = ecs_get_id(world, e, ecs_pair(m, ecs_id(EcsMetricValue)));
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e2);
            test_int(((double*)inst)[0], 0);
            test_int(((double*)inst)[1], 1);
            test_int(((double*)inst)[2], 0);
        }
        {
            ecs_entity_t e = it.entities[2];
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const void *inst = ecs_get_id(world, e, ecs_pair(m, ecs_id(EcsMetricValue)));
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e3);
            test_int(((double*)inst)[0], 0);
            test_int(((double*)inst)[1], 0);
            test_int(((double*)inst)[2], 1);
        }
        test_bool(false, ecs_filter_next(&it));
    }

    ecs_remove_pair(world, e2, Color, EcsWildcard);
    ecs_progress(world, 0);

    {
        ecs_iter_t it = ecs_filter_iter(world, f);
        test_bool(true, ecs_filter_next(&it));
        test_uint(it.count, 2);
        {
            ecs_entity_t e = it.entities[0];
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const void *inst = ecs_get_id(world, e, ecs_pair(m, ecs_id(EcsMetricValue)));
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e1);
            test_int(((double*)inst)[0], 1);
            test_int(((double*)inst)[1], 0);
            test_int(((double*)inst)[2], 0);
        }
        {
            ecs_entity_t e = it.entities[1];
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const void *inst = ecs_get_id(world, e, ecs_pair(m, ecs_id(EcsMetricValue)));
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e3);
            test_int(((double*)inst)[0], 0);
            test_int(((double*)inst)[1], 0);
            test_int(((double*)inst)[2], 1);
        }
        test_bool(false, ecs_filter_next(&it));
    }

    ecs_filter_fini(f);

    ecs_fini(world);
}

void Metrics_oneof_gauge_w_clear(void) {
    ecs_world_t *world = ecs_init();
    ECS_IMPORT(world, FlecsMetrics);

    ECS_ENTITY(world, Color, OneOf, Exclusive);
    ECS_ENTITY(world, Red,   (ChildOf, Color));
    ECS_ENTITY(world, Green, (ChildOf, Color));
    ECS_ENTITY(world, Blue,  (ChildOf, Color));
    ECS_TAG(world, Foo);

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.color" }),
        .id = ecs_pair(Color, EcsWildcard),
        .targets = true,
        .kind = EcsGauge
    });
    test_assert(m != 0);

    ecs_entity_t e1 = ecs_new_w_pair(world, Color, Red);
    ecs_entity_t e2 = ecs_new_w_pair(world, Color, Green);
    ecs_entity_t e3 = ecs_new_w_pair(world, Color, Blue);
    ecs_add(world, e1, Foo);
    ecs_add(world, e2, Foo);
    ecs_add(world, e3, Foo);
    
    ecs_progress(world, 0);

    ecs_filter_t *f = ecs_filter(world, {
        .terms = {
            { .id = ecs_childof(m) },
            { .id = ecs_pair(EcsMetric, EcsGauge) }
        }
    });
    test_assert(f != NULL);

    {
        ecs_iter_t it = ecs_filter_iter(world, f);
        test_bool(true, ecs_filter_next(&it));
        test_uint(it.count, 3);
        {
            ecs_entity_t e = it.entities[0];
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const void *inst = ecs_get_id(world, e, ecs_pair(m, ecs_id(EcsMetricValue)));
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e1);
            test_int(((double*)inst)[0], 1);
            test_int(((double*)inst)[1], 0);
            test_int(((double*)inst)[2], 0);
        }
        {
            ecs_entity_t e = it.entities[1];
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const void *inst = ecs_get_id(world, e, ecs_pair(m, ecs_id(EcsMetricValue)));
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e2);
            test_int(((double*)inst)[0], 0);
            test_int(((double*)inst)[1], 1);
            test_int(((double*)inst)[2], 0);
        }
        {
            ecs_entity_t e = it.entities[2];
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const void *inst = ecs_get_id(world, e, ecs_pair(m, ecs_id(EcsMetricValue)));
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e3);
            test_int(((double*)inst)[0], 0);
            test_int(((double*)inst)[1], 0);
            test_int(((double*)inst)[2], 1);
        }
        test_bool(false, ecs_filter_next(&it));
    }

    ecs_clear(world, e2);
    ecs_progress(world, 0);

    {
        ecs_iter_t it = ecs_filter_iter(world, f);
        test_bool(true, ecs_filter_next(&it));
        test_uint(it.count, 2);
        {
            ecs_entity_t e = it.entities[0];
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const void *inst = ecs_get_id(world, e, ecs_pair(m, ecs_id(EcsMetricValue)));
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e1);
            test_int(((double*)inst)[0], 1);
            test_int(((double*)inst)[1], 0);
            test_int(((double*)inst)[2], 0);
        }
        {
            ecs_entity_t e = it.entities[1];
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const void *inst = ecs_get_id(world, e, ecs_pair(m, ecs_id(EcsMetricValue)));
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e3);
            test_int(((double*)inst)[0], 0);
            test_int(((double*)inst)[1], 0);
            test_int(((double*)inst)[2], 1);
        }
        test_bool(false, ecs_filter_next(&it));
    }

    ecs_filter_fini(f);

    ecs_fini(world);
}

void Metrics_oneof_gauge_w_delete(void) {
    ecs_world_t *world = ecs_init();
    ECS_IMPORT(world, FlecsMetrics);

    ECS_ENTITY(world, Color, OneOf, Exclusive);
    ECS_ENTITY(world, Red,   (ChildOf, Color));
    ECS_ENTITY(world, Green, (ChildOf, Color));
    ECS_ENTITY(world, Blue,  (ChildOf, Color));
    ECS_TAG(world, Foo);

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.color" }),
        .id = ecs_pair(Color, EcsWildcard),
        .targets = true,
        .kind = EcsGauge
    });
    test_assert(m != 0);

    ecs_entity_t e1 = ecs_new_w_pair(world, Color, Red);
    ecs_entity_t e2 = ecs_new_w_pair(world, Color, Green);
    ecs_entity_t e3 = ecs_new_w_pair(world, Color, Blue);
    ecs_add(world, e1, Foo);
    ecs_add(world, e2, Foo);
    ecs_add(world, e3, Foo);
    
    ecs_progress(world, 0);

    ecs_filter_t *f = ecs_filter(world, {
        .terms = {
            { .id = ecs_childof(m) },
            { .id = ecs_pair(EcsMetric, EcsGauge) }
        }
    });
    test_assert(f != NULL);

    {
        ecs_iter_t it = ecs_filter_iter(world, f);
        test_bool(true, ecs_filter_next(&it));
        test_uint(it.count, 3);
        {
            ecs_entity_t e = it.entities[0];
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const void *inst = ecs_get_id(world, e, ecs_pair(m, ecs_id(EcsMetricValue)));
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e1);
            test_int(((double*)inst)[0], 1);
            test_int(((double*)inst)[1], 0);
            test_int(((double*)inst)[2], 0);
        }
        {
            ecs_entity_t e = it.entities[1];
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const void *inst = ecs_get_id(world, e, ecs_pair(m, ecs_id(EcsMetricValue)));
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e2);
            test_int(((double*)inst)[0], 0);
            test_int(((double*)inst)[1], 1);
            test_int(((double*)inst)[2], 0);
        }
        {
            ecs_entity_t e = it.entities[2];
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const void *inst = ecs_get_id(world, e, ecs_pair(m, ecs_id(EcsMetricValue)));
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e3);
            test_int(((double*)inst)[0], 0);
            test_int(((double*)inst)[1], 0);
            test_int(((double*)inst)[2], 1);
        }
        test_bool(false, ecs_filter_next(&it));
    }

    ecs_delete(world, e2);
    ecs_progress(world, 0);

    {
        ecs_iter_t it = ecs_filter_iter(world, f);
        test_bool(true, ecs_filter_next(&it));
        test_uint(it.count, 2);
        {
            ecs_entity_t e = it.entities[0];
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const void *inst = ecs_get_id(world, e, ecs_pair(m, ecs_id(EcsMetricValue)));
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e1);
            test_int(((double*)inst)[0], 1);
            test_int(((double*)inst)[1], 0);
            test_int(((double*)inst)[2], 0);
        }
        {
            ecs_entity_t e = it.entities[1];
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const void *inst = ecs_get_id(world, e, ecs_pair(m, ecs_id(EcsMetricValue)));
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e3);
            test_int(((double*)inst)[0], 0);
            test_int(((double*)inst)[1], 0);
            test_int(((double*)inst)[2], 1);
        }
        test_bool(false, ecs_filter_next(&it));
    }

    ecs_filter_fini(f);

    ecs_fini(world);
}

void Metrics_oneof_to_snake_case(void) {
    ecs_world_t *world = ecs_init();
    ECS_IMPORT(world, FlecsMetrics);

    ECS_ENTITY(world, Color, OneOf, Exclusive);
    ECS_ENTITY(world, RedColor,    (ChildOf, Color));
    ECS_ENTITY(world, Green_Color, (ChildOf, Color));
    ECS_ENTITY(world, Blue,        (ChildOf, Color));
    ECS_TAG(world, Foo);

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.color" }),
        .id = ecs_pair(Color, EcsWildcard),
        .targets = true,
        .kind = EcsGauge
    });
    test_assert(m != 0);

    const EcsStruct *s = ecs_get(world, m, EcsStruct);
    test_assert(s != NULL);
    test_int(s->members.count, 3);

    ecs_member_t *members = s->members.array;
    test_str(members[0].name, "red_color");
    test_str(members[1].name, "green_color");
    test_str(members[2].name, "blue");

    ecs_fini(world);
}

void Metrics_member_counter(void) {
    ecs_world_t *world = ecs_init();
    ECS_IMPORT(world, FlecsMetrics);

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            { "x", ecs_id(ecs_f32_t) },
            { "y", ecs_id(ecs_f32_t) },
        }
    });

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.position_y" }),
        .member = ecs_lookup_fullpath(world, "Position.y"),
        .kind = EcsCounter
    });
    test_assert(m != 0);

    ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20});
    ecs_progress(world, 1.0);
    
    {
        ecs_iter_t it = ecs_children(world, m);
        test_bool(true, ecs_children_next(&it));
        test_uint(it.count, 1);
        {
            ecs_entity_t e = it.entities[0];
            test_assert(ecs_has_pair(world, e, EcsMetric, EcsCounter));
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e1);
            test_int(inst->value, 20);
        }
        test_bool(false, ecs_children_next(&it));
    }

    ecs_progress(world, 1.0);

    {
        ecs_iter_t it = ecs_children(world, m);
        test_bool(true, ecs_children_next(&it));
        test_uint(it.count, 1);
        {
            ecs_entity_t e = it.entities[0];
            test_assert(ecs_has_pair(world, e, EcsMetric, EcsCounter));
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e1);
            test_int(inst->value, 20);
        }
        test_bool(false, ecs_children_next(&it));
    }

    ecs_fini(world);
}

void Metrics_member_auto_counter(void) {
    ecs_world_t *world = ecs_init();
    ECS_IMPORT(world, FlecsMetrics);

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            { "x", ecs_id(ecs_f32_t) },
            { "y", ecs_id(ecs_f32_t) },
        }
    });

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.position_y" }),
        .member = ecs_lookup_fullpath(world, "Position.y"),
        .kind = EcsCounterIncrement
    });
    test_assert(m != 0);

    ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20});
    ecs_progress(world, 1.0);
    
    {
        ecs_iter_t it = ecs_children(world, m);
        test_bool(true, ecs_children_next(&it));
        test_uint(it.count, 1);
        {
            ecs_entity_t e = it.entities[0];
            test_assert(ecs_has_pair(world, e, EcsMetric, EcsCounterIncrement));
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e1);
            test_int(inst->value, 20);
        }
        test_bool(false, ecs_children_next(&it));
    }

    ecs_progress(world, 1.0);

    {
        ecs_iter_t it = ecs_children(world, m);
        test_bool(true, ecs_children_next(&it));
        test_uint(it.count, 1);
        {
            ecs_entity_t e = it.entities[0];
            test_assert(ecs_has_pair(world, e, EcsMetric, EcsCounterIncrement));
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e1);
            test_int(inst->value, 40);
        }
        test_bool(false, ecs_children_next(&it));
    }

    ecs_fini(world);
}

void Metrics_id_counter(void) {
    ecs_world_t *world = ecs_init();
    ECS_IMPORT(world, FlecsMetrics);

    ECS_TAG(world, Foo);

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.has_foo" }),
        .id = Foo,
        .kind = EcsCounter
    });
    test_assert(m != 0);

    ecs_entity_t e1 = ecs_new(world, Foo);

    ecs_progress(world, 1.0);

    {
        ecs_iter_t it = ecs_children(world, m);
        test_bool(true, ecs_children_next(&it));
        test_uint(it.count, 1);
        {
            ecs_entity_t e = it.entities[0];
            test_assert(ecs_has_pair(world, e, EcsMetric, EcsCounter));
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e1);
            test_int(inst->value, 1);
        }
        test_bool(false, ecs_children_next(&it));
    }
    
    ecs_progress(world, 1.0);

    {
        ecs_iter_t it = ecs_children(world, m);
        test_bool(true, ecs_children_next(&it));
        test_uint(it.count, 1);
        {
            ecs_entity_t e = it.entities[0];
            test_assert(ecs_has_pair(world, e, EcsMetric, EcsCounter));
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e1);
            test_int(inst->value, 2);
        }
        test_bool(false, ecs_children_next(&it));
    }

    ecs_fini(world);
}

void Metrics_oneof_counter(void) {
    ecs_world_t *world = ecs_init();
    ECS_IMPORT(world, FlecsMetrics);

    ECS_ENTITY(world, Color, OneOf, Exclusive);
    ECS_ENTITY(world, Red,   (ChildOf, Color));
    ECS_ENTITY(world, Green, (ChildOf, Color));
    ECS_ENTITY(world, Blue,  (ChildOf, Color));

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.color" }),
        .id = ecs_pair(Color, EcsWildcard),
        .targets = true,
        .kind = EcsCounter
    });
    test_assert(m != 0);

    ecs_entity_t e1 = ecs_new_w_pair(world, Color, Red);

    ecs_progress(world, 1.0);

    ecs_filter_t *f = ecs_filter(world, {
        .terms = {
            { .id = ecs_childof(m) },
            { .id = ecs_pair(EcsMetric, EcsCounter) }
        }
    });
    test_assert(f != NULL);

    {
        ecs_iter_t it = ecs_filter_iter(world, f);
        test_bool(true, ecs_filter_next(&it));
        test_uint(it.count, 1);
        {
            ecs_entity_t e = it.entities[0];
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const void *inst = ecs_get_id(world, e, ecs_pair(m, ecs_id(EcsMetricValue)));
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e1);
            test_int(((double*)inst)[0], 1);
            test_int(((double*)inst)[1], 0);
            test_int(((double*)inst)[2], 0);
        }
        test_bool(false, ecs_filter_next(&it));
    }

    ecs_add_pair(world, e1, Color, Green);
    ecs_progress(world, 1.0);

    {
        ecs_iter_t it = ecs_filter_iter(world, f);
        test_bool(true, ecs_filter_next(&it));
        test_uint(it.count, 1);
        {
            ecs_entity_t e = it.entities[0];
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const void *inst = ecs_get_id(world, e, ecs_pair(m, ecs_id(EcsMetricValue)));
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e1);
            test_int(((double*)inst)[0], 1);
            test_int(((double*)inst)[1], 1);
            test_int(((double*)inst)[2], 0);
        }
        test_bool(false, ecs_filter_next(&it));
    }

    ecs_add_pair(world, e1, Color, Blue);
    ecs_progress(world, 1.0);

    {
        ecs_iter_t it = ecs_filter_iter(world, f);
        test_bool(true, ecs_filter_next(&it));
        test_uint(it.count, 1);
        {
            ecs_entity_t e = it.entities[0];
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const void *inst = ecs_get_id(world, e, ecs_pair(m, ecs_id(EcsMetricValue)));
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e1);
            test_int(((double*)inst)[0], 1);
            test_int(((double*)inst)[1], 1);
            test_int(((double*)inst)[2], 1);
        }
        test_bool(false, ecs_filter_next(&it));
    }

    ecs_progress(world, 1.0);

    {
        ecs_iter_t it = ecs_filter_iter(world, f);
        test_bool(true, ecs_filter_next(&it));
        test_uint(it.count, 1);
        {
            ecs_entity_t e = it.entities[0];
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const void *inst = ecs_get_id(world, e, ecs_pair(m, ecs_id(EcsMetricValue)));
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e1);
            test_int(((double*)inst)[0], 1);
            test_int(((double*)inst)[1], 1);
            test_int(((double*)inst)[2], 2);
        }
        test_bool(false, ecs_filter_next(&it));
    }

    ecs_filter_fini(f);

    ecs_fini(world);
}

void Metrics_metric_description(void) {
    ecs_world_t *world = ecs_init();

    ECS_IMPORT(world, FlecsMetrics);

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            { "x", ecs_id(ecs_f32_t) },
            { "y", ecs_id(ecs_f32_t) },
        }
    });

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.position_y" }),
        .member = ecs_lookup_fullpath(world, "Position.y"),
        .kind = EcsGauge,
        .brief = "Position y"
    });
    test_assert(m != 0);

    test_str(ecs_doc_get_brief(world, m), "Position y");

    ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20});

    ecs_progress(world, 0);
    
    ecs_iter_t it = ecs_children(world, m);
    test_bool(true, ecs_children_next(&it));
    test_uint(it.count, 1);
    {
        ecs_entity_t e = it.entities[0];
        test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
        const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
        const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
        test_assert(src != NULL);
        test_assert(inst != NULL);
        test_uint(src->entity, e1);
        test_int(inst->value, 20);
    }

    test_bool(false, ecs_children_next(&it));

    ecs_fini(world);
}

void Metrics_id_count(void) {
    ecs_world_t *world = ecs_init();

    ECS_IMPORT(world, FlecsMetrics);

    ECS_COMPONENT(world, Position);

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.position" }),
        .id = ecs_id(Position),
        .kind = EcsCounterId
    });
    test_assert(m != 0);

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

    ecs_progress(world, 1);

    {
        const EcsMetricValue *v = ecs_get(world, m, EcsMetricValue);
        test_assert(v != NULL);
        test_int(v->value, 1);
    }

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

    ecs_progress(world, 1);

    {
        const EcsMetricValue *v = ecs_get(world, m, EcsMetricValue);
        test_assert(v != NULL);
        test_int(v->value, 4);
    }

    ecs_fini(world);
}

void Metrics_id_target_count(void) {
    ecs_world_t *world = ecs_init();

    ECS_IMPORT(world, FlecsMetrics);

    ECS_TAG(world, Color);
    ECS_TAG(world, Red);
    ECS_TAG(world, Green);
    ECS_TAG(world, Blue);

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.color" }),
        .id = ecs_pair(Color, EcsWildcard),
        .targets = true,
        .kind = EcsCounterId
    });
    test_assert(m != 0);

    ecs_entity_t e1 = ecs_new_id(world);
    ecs_add_pair(world, e1, Color, Red);
    ecs_entity_t e2 = ecs_new_id(world);
    ecs_add_pair(world, e2, Color, Green);
    ecs_add_pair(world, e2, Color, Blue);
    ecs_entity_t e3 = ecs_new_id(world);
    ecs_add_pair(world, e3, Color, Blue);

    ecs_progress(world, 1);

    {
        ecs_entity_t red = ecs_lookup_fullpath(world, "metrics.color.Red");
        test_assert(red != 0);
        const EcsMetricValue *v = ecs_get(world, red, EcsMetricValue);
        test_assert(v != NULL);
        test_int(v->value, 1);
    }

    {
        ecs_entity_t green = ecs_lookup_fullpath(world, "metrics.color.Green");
        test_assert(green != 0);
        const EcsMetricValue *v = ecs_get(world, green, EcsMetricValue);
        test_assert(v != NULL);
        test_int(v->value, 1);
    }

    {
        ecs_entity_t blue = ecs_lookup_fullpath(world, "metrics.color.Blue");
        test_assert(blue != 0);
        const EcsMetricValue *v = ecs_get(world, blue, EcsMetricValue);
        test_assert(v != NULL);
        test_int(v->value, 2);
    }

    ecs_fini(world);
}

void Metrics_metric_instance_has_doc_name(void) {
    ecs_world_t *world = ecs_init();
    ECS_IMPORT(world, FlecsMetrics);

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            { "x", ecs_id(ecs_f32_t) },
            { "y", ecs_id(ecs_f32_t) },
        }
    });

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.position_y" }),
        .member = ecs_lookup_fullpath(world, "Position.y"),
        .kind = EcsGauge
    });
    test_assert(m != 0);

    ecs_entity_t e1 = ecs_new_entity(world, "e1");
    ecs_set(world, e1, Position, {10, 20});

    ecs_progress(world, 0);
    
    ecs_iter_t it = ecs_children(world, m);
    test_bool(true, ecs_children_next(&it));
    test_uint(it.count, 1);
    {
        ecs_entity_t e = it.entities[0];
        test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
        const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
        const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
        test_assert(src != NULL);
        test_assert(inst != NULL);
        test_uint(src->entity, e1);
        test_int(inst->value, 20);

        test_str(ecs_doc_get_name(world, e), "e1");
    }

    test_bool(false, ecs_children_next(&it));

    ecs_fini(world);
}

void Metrics_metric_nested_member(void) {
    typedef struct {
        float x, y;
    } Point;

    typedef struct {
        float dummy;
        Point position;
    } Position;

    ecs_world_t *world = ecs_init();
    ECS_IMPORT(world, FlecsMetrics);

    ECS_COMPONENT(world, Point);
    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Point),
        .members = {
            { "x", ecs_id(ecs_f32_t) },
            { "y", ecs_id(ecs_f32_t) },
        }
    });

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            { "dummy", ecs_id(ecs_f32_t) },
            { "position", ecs_id(Point) },
        }
    });

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.position_y" }),
        .id = ecs_id(Position),
        .dotmember = "position.y",
        .kind = EcsGauge
    });

    test_assert(m != 0);

    ecs_entity_t e1 = ecs_new_entity(world, "e1");
    ecs_set(world, e1, Position, {10, {20, 30}});

    ecs_progress(world, 0);
    
    ecs_iter_t it = ecs_children(world, m);
    test_bool(true, ecs_children_next(&it));
    test_uint(it.count, 1);
    {
        ecs_entity_t e = it.entities[0];
        test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
        const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
        const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
        test_assert(src != NULL);
        test_assert(inst != NULL);
        test_uint(src->entity, e1);
        test_int(inst->value, 30);

        test_str(ecs_doc_get_name(world, e), "e1");
    }

    test_bool(false, ecs_children_next(&it));

    ecs_fini(world);
}

void Metrics_metric_nested_member_counter(void) {
    typedef struct {
        float x, y;
    } Point;

    typedef struct {
        float dummy;
        Point position;
    } Position;

    ecs_world_t *world = ecs_init();
    ECS_IMPORT(world, FlecsMetrics);

    ECS_COMPONENT(world, Point);
    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Point),
        .members = {
            { "x", ecs_id(ecs_f32_t) },
            { "y", ecs_id(ecs_f32_t) },
        }
    });

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            { "dummy", ecs_id(ecs_f32_t) },
            { "position", ecs_id(Point) },
        }
    });

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.position_y" }),
        .id = ecs_id(Position),
        .dotmember = "position.y",
        .kind = EcsCounter
    });

    test_assert(m != 0);

    ecs_entity_t e1 = ecs_new_entity(world, "e1");
    ecs_set(world, e1, Position, {10, {20, 30}});

    ecs_progress(world, 0);
    
    ecs_iter_t it = ecs_children(world, m);
    test_bool(true, ecs_children_next(&it));
    test_uint(it.count, 1);
    {
        ecs_entity_t e = it.entities[0];
        test_assert(ecs_has_pair(world, e, EcsMetric, EcsCounter));
        const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
        const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
        test_assert(src != NULL);
        test_assert(inst != NULL);
        test_uint(src->entity, e1);
        test_int(inst->value, 30);

        test_str(ecs_doc_get_name(world, e), "e1");
    }

    test_bool(false, ecs_children_next(&it));

    ecs_fini(world);
}

void Metrics_metric_nested_member_counter_increment(void) {
    typedef struct {
        float x, y;
    } Point;

    typedef struct {
        float dummy;
        Point position;
    } Position;

    ecs_world_t *world = ecs_init();
    ECS_IMPORT(world, FlecsMetrics);

    ECS_COMPONENT(world, Point);
    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Point),
        .members = {
            { "x", ecs_id(ecs_f32_t) },
            { "y", ecs_id(ecs_f32_t) },
        }
    });

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            { "dummy", ecs_id(ecs_f32_t) },
            { "position", ecs_id(Point) },
        }
    });

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.position_y" }),
        .id = ecs_id(Position),
        .dotmember = "position.y",
        .kind = EcsCounterIncrement
    });

    test_assert(m != 0);

    ecs_entity_t e1 = ecs_new_entity(world, "e1");
    ecs_set(world, e1, Position, {10, {20, 30}});

    ecs_progress(world, 1);
    
    {
        ecs_iter_t it = ecs_children(world, m);
        test_bool(true, ecs_children_next(&it));
        test_uint(it.count, 1);
        {
            ecs_entity_t e = it.entities[0];
            test_assert(ecs_has_pair(world, e, EcsMetric, EcsCounterIncrement));
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e1);
            test_int(inst->value, 30);

            test_str(ecs_doc_get_name(world, e), "e1");
        }

        test_bool(false, ecs_children_next(&it));
    }

    ecs_progress(world, 1);

    {
        ecs_iter_t it = ecs_children(world, m);
        test_bool(true, ecs_children_next(&it));
        test_uint(it.count, 1);
        {
            ecs_entity_t e = it.entities[0];
            test_assert(ecs_has_pair(world, e, EcsMetric, EcsCounterIncrement));
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e1);
            test_int(inst->value, 60);

            test_str(ecs_doc_get_name(world, e), "e1");
        }

        test_bool(false, ecs_children_next(&it));
    }

    ecs_fini(world);
}

void Metrics_id_w_member_same_type(void) {
    ecs_world_t *world = ecs_init();
    ECS_IMPORT(world, FlecsMetrics);

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            { "x", ecs_id(ecs_f32_t) },
            { "y", ecs_id(ecs_f32_t) },
        }
    });

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.position_y" }),
        .id = ecs_id(Position),
        .member = ecs_lookup_fullpath(world, "Position.y"),
        .kind = EcsGauge
    });
    test_assert(m != 0);

    ecs_entity_t e1 = ecs_set(world, 0, Position, {10, 20});

    ecs_progress(world, 0);
    
    ecs_iter_t it = ecs_children(world, m);
    test_bool(true, ecs_children_next(&it));
    test_uint(it.count, 1);
    {
        ecs_entity_t e = it.entities[0];
        test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
        const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
        const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
        test_assert(src != NULL);
        test_assert(inst != NULL);
        test_uint(src->entity, e1);
        test_int(inst->value, 20);
    }

    test_bool(false, ecs_children_next(&it));

    ecs_fini(world);
}

void Metrics_id_w_member_mismatching_type(void) {
    ecs_world_t *world = ecs_init();
    ECS_IMPORT(world, FlecsMetrics);

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

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            { "x", ecs_id(ecs_f32_t) },
            { "y", ecs_id(ecs_f32_t) },
        }
    });

    ecs_log_set_level(-4);
    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.position_y" }),
        .id = ecs_id(Velocity),
        .member = ecs_lookup_fullpath(world, "Position.y"),
        .kind = EcsGauge
    });
    test_assert(m == 0);

    ecs_fini(world);
}

void Metrics_pair_member_rel_type(void) {
    ecs_world_t *world = ecs_init();
    ECS_IMPORT(world, FlecsMetrics);

    ECS_COMPONENT(world, Position);
    ECS_TAG(world, Foo);
    ECS_TAG(world, Bar);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            { "x", ecs_id(ecs_f32_t) },
            { "y", ecs_id(ecs_f32_t) },
        }
    });

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.position_y" }),
        .id = ecs_pair_t(Position, Foo),
        .member = ecs_lookup_fullpath(world, "Position.y"),
        .kind = EcsGauge
    });
    test_assert(m != 0);

    ecs_entity_t e1 = ecs_set_pair(world, 0, Position, Foo, {10, 20});
    ecs_set_pair(world, e1, Position, Bar, {1, 2});

    ecs_progress(world, 0);
    
    ecs_iter_t it = ecs_children(world, m);
    test_bool(true, ecs_children_next(&it));
    test_uint(it.count, 1);
    {
        ecs_entity_t e = it.entities[0];
        test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
        const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
        const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
        test_assert(src != NULL);
        test_assert(inst != NULL);
        test_uint(src->entity, e1);
        test_int(inst->value, 20);
    }

    test_bool(false, ecs_children_next(&it));

    ecs_fini(world);
}

void Metrics_pair_member_tgt_type(void) {
    ecs_world_t *world = ecs_init();
    ECS_IMPORT(world, FlecsMetrics);

    ECS_COMPONENT(world, Position);
    ECS_TAG(world, Foo);
    ECS_TAG(world, Bar);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            { "x", ecs_id(ecs_f32_t) },
            { "y", ecs_id(ecs_f32_t) },
        }
    });

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.position_y" }),
        .id = ecs_pair(Foo, ecs_id(Position)),
        .member = ecs_lookup_fullpath(world, "Position.y"),
        .kind = EcsGauge
    });
    test_assert(m != 0);

    ecs_entity_t e1 = ecs_set_pair_second(world, 0, Foo, Position, {10, 20});
    ecs_set_pair_second(world, e1, Bar, Position, {1, 2});

    ecs_progress(world, 0);
    
    ecs_iter_t it = ecs_children(world, m);
    test_bool(true, ecs_children_next(&it));
    test_uint(it.count, 1);
    {
        ecs_entity_t e = it.entities[0];
        test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
        const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
        const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
        test_assert(src != NULL);
        test_assert(inst != NULL);
        test_uint(src->entity, e1);
        test_int(inst->value, 20);
    }

    test_bool(false, ecs_children_next(&it));

    ecs_fini(world);
}

void Metrics_pair_dotmember_rel_type(void) {
    typedef struct {
        float x, y;
    } Point;

    typedef struct {
        float dummy;
        Point position;
    } Position;

    ecs_world_t *world = ecs_init();
    ECS_IMPORT(world, FlecsMetrics);

    ECS_COMPONENT(world, Point);
    ECS_COMPONENT(world, Position);
    ECS_TAG(world, Foo);
    ECS_TAG(world, Bar);

    ecs_struct(world, {
        .entity = ecs_id(Point),
        .members = {
            { "x", ecs_id(ecs_f32_t) },
            { "y", ecs_id(ecs_f32_t) },
        }
    });

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            { "dummy", ecs_id(ecs_f32_t) },
            { "position", ecs_id(Point) },
        }
    });

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.position_y" }),
        .id = ecs_pair_t(Position, Foo),
        .dotmember = "position.y",
        .kind = EcsGauge
    });
    test_assert(m != 0);

    ecs_entity_t e1 = ecs_set_pair(world, 0, Position, Foo, {0, {10, 20}});
    ecs_set_pair(world, e1, Position, Bar, {0, {1, 2}});

    ecs_progress(world, 0);
    
    ecs_iter_t it = ecs_children(world, m);
    test_bool(true, ecs_children_next(&it));
    test_uint(it.count, 1);
    {
        ecs_entity_t e = it.entities[0];
        test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
        const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
        const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
        test_assert(src != NULL);
        test_assert(inst != NULL);
        test_uint(src->entity, e1);
        test_int(inst->value, 20);
    }

    test_bool(false, ecs_children_next(&it));

    ecs_fini(world);
}

void Metrics_pair_dotmember_tgt_type(void) {
    typedef struct {
        float x, y;
    } Point;

    typedef struct {
        float dummy;
        Point position;
    } Position;

    ecs_world_t *world = ecs_init();
    ECS_IMPORT(world, FlecsMetrics);

    ECS_COMPONENT(world, Point);
    ECS_COMPONENT(world, Position);
    ECS_TAG(world, Foo);
    ECS_TAG(world, Bar);

    ecs_struct(world, {
        .entity = ecs_id(Point),
        .members = {
            { "x", ecs_id(ecs_f32_t) },
            { "y", ecs_id(ecs_f32_t) },
        }
    });

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            { "dummy", ecs_id(ecs_f32_t) },
            { "position", ecs_id(Point) },
        }
    });

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.position_y" }),
        .id = ecs_pair(Foo, ecs_id(Position)),
        .dotmember = "position.y",
        .kind = EcsGauge
    });
    test_assert(m != 0);

    ecs_entity_t e1 = ecs_set_pair_second(world, 0, Foo, Position, {0, {10, 20}});
    ecs_set_pair_second(world, e1, Bar, Position, {0, {1, 2}});

    ecs_progress(world, 0);
    
    ecs_iter_t it = ecs_children(world, m);
    test_bool(true, ecs_children_next(&it));
    test_uint(it.count, 1);
    {
        ecs_entity_t e = it.entities[0];
        test_assert(ecs_has_pair(world, e, EcsMetric, EcsGauge));
        const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
        const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
        test_assert(src != NULL);
        test_assert(inst != NULL);
        test_uint(src->entity, e1);
        test_int(inst->value, 20);
    }

    test_bool(false, ecs_children_next(&it));

    ecs_fini(world);
}

void Metrics_pair_member_counter_increment(void) {
    ecs_world_t *world = ecs_init();
    ECS_IMPORT(world, FlecsMetrics);

    ECS_COMPONENT(world, Position);
    ECS_TAG(world, Foo);
    ECS_TAG(world, Bar);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            { "x", ecs_id(ecs_f32_t) },
            { "y", ecs_id(ecs_f32_t) },
        }
    });

    ecs_entity_t m = ecs_metric(world, {
        .entity = ecs_entity(world, { .name = "metrics.position_y" }),
        .id = ecs_pair_t(Position, Foo),
        .member = ecs_lookup_fullpath(world, "Position.y"),
        .kind = EcsCounterIncrement
    });
    test_assert(m != 0);

    ecs_entity_t e1 = ecs_set_pair(world, 0, Position, Foo, {10, 20});
    ecs_set_pair(world, e1, Position, Bar, {1, 2});

    ecs_progress(world, 1);
    
    {
        ecs_iter_t it = ecs_children(world, m);
        test_bool(true, ecs_children_next(&it));
        test_uint(it.count, 1);
        {
            ecs_entity_t e = it.entities[0];
            test_assert(ecs_has_pair(world, e, EcsMetric, EcsCounterIncrement));
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e1);
            test_int(inst->value, 20);
        }

        test_bool(false, ecs_children_next(&it));
    }

    ecs_progress(world, 1);

    {
        ecs_iter_t it = ecs_children(world, m);
        test_bool(true, ecs_children_next(&it));
        test_uint(it.count, 1);
        {
            ecs_entity_t e = it.entities[0];
            test_assert(ecs_has_pair(world, e, EcsMetric, EcsCounterIncrement));
            const EcsMetricSource *src = ecs_get(world, e, EcsMetricSource);
            const EcsMetricValue *inst = ecs_get(world, e, EcsMetricValue);
            test_assert(src != NULL);
            test_assert(inst != NULL);
            test_uint(src->entity, e1);
            test_int(inst->value, 40);
        }

        test_bool(false, ecs_children_next(&it));
    }

    ecs_fini(world);
}
