#include void Alerts_one_active_alert(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t e1 = ecs_new_entity(world, "e1"); ecs_entity_t e2 = ecs_new_entity(world, "e2"); ecs_add(world, e1, Position); ecs_add(world, e1, Velocity); ecs_add(world, e2, Position); ecs_entity_t alert = ecs_alert(world, { .entity = ecs_new_entity(world, "position_without_velocity"), .filter.expr = "Position, !Velocity" }); test_assert(alert != 0); ecs_progress(world, 1.0); test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_assert(ecs_has(world, e2, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e2, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertError)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e2); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_progress(world, 1.0); /* Verify there is still only one alert */ test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_assert(ecs_has(world, e2, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e2, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertError)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e2); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_add(world, e2, Velocity); ecs_progress(world, 1.0); /* Verify that alert has cleared */ test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_assert(!ecs_has(world, e2, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 0); ecs_fini(world); } void Alerts_two_active_alerts(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); ecs_entity_t e1 = ecs_new_entity(world, "e1"); ecs_entity_t e2 = ecs_new_entity(world, "e2"); ecs_add(world, e1, Position); ecs_add(world, e1, Velocity); ecs_add(world, e1, Mass); ecs_add(world, e2, Position); ecs_entity_t alert_1 = ecs_alert(world, { .entity = ecs_new_entity(world, "position_without_velocity"), .filter.expr = "Position, !Velocity" }); ecs_entity_t alert_2 = ecs_alert(world, { .entity = ecs_new_entity(world, "position_without_mass"), .filter.expr = "Position, !Mass" }); ecs_progress(world, 1.0); test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_assert(ecs_has(world, e2, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 2); { test_assert(ecs_get_alert_count(world, e2, alert_1) == 1); test_assert(ecs_get_alert_count(world, e2, alert_2) == 1); test_assert(ecs_get_alert_count(world, e2, 0) == 2); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert_1); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertError)); { const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e2); } test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert_2); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertError)); { const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e2); } test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_progress(world, 1.0); /* Verify there are still only two alerts */ test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_assert(ecs_has(world, e2, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 2); { test_assert(ecs_get_alert_count(world, e2, alert_1) == 1); test_assert(ecs_get_alert_count(world, e2, alert_2) == 1); test_assert(ecs_get_alert_count(world, e2, 0) == 2); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert_1); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertError)); { const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e2); } test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert_2); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertError)); { const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e2); } test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_add(world, e2, Mass); ecs_progress(world, 1.0); /* Verify there is only one alert left */ test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_assert(ecs_has(world, e2, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e2, alert_1) == 1); test_assert(ecs_get_alert_count(world, e2, alert_2) == 0); test_assert(ecs_get_alert_count(world, e2, 0) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert_1); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertError)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e2); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_add(world, e2, Velocity); ecs_progress(world, 1.0); /* Verify that alert has cleared */ test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_assert(!ecs_has(world, e2, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 0); ecs_fini(world); } void Alerts_alert_message(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t e1 = ecs_new_entity(world, "e1"); ecs_entity_t e2 = ecs_new_entity(world, "e2"); ecs_add(world, e1, Position); ecs_add(world, e1, Velocity); ecs_add(world, e2, Position); ecs_entity_t alert = ecs_alert(world, { .entity = ecs_new_entity(world, "position_without_velocity"), .filter.expr = "Position, !Velocity", .message = "missing velocity" }); test_assert(alert != 0); ecs_progress(world, 1.0); test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_assert(ecs_has(world, e2, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e2, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertError)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); test_str(instance->message, "missing velocity"); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e2); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_fini(world); } void Alerts_alert_message_w_this_var(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t e1 = ecs_new_entity(world, "e1"); ecs_entity_t e2 = ecs_new_entity(world, "e2"); ecs_add(world, e1, Position); ecs_add(world, e1, Velocity); ecs_add(world, e2, Position); ecs_entity_t alert = ecs_alert(world, { .entity = ecs_new_entity(world, "position_without_velocity"), .filter.expr = "Position, !Velocity", .message = "$this: missing velocity" }); test_assert(alert != 0); ecs_progress(world, 1.0); test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_assert(ecs_has(world, e2, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e2, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertError)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); test_str(instance->message, "e2: missing velocity"); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e2); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_fini(world); } void Alerts_alert_message_w_var(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Position); ecs_entity_t parent_1 = ecs_new_entity(world, "p1"); ecs_add(world, parent_1, Position); ecs_entity_t parent_2 = ecs_new_entity(world, "p2"); ecs_entity_t e1 = ecs_new_entity(world, "e1"); ecs_entity_t e2 = ecs_new_entity(world, "e2"); ecs_add_pair(world, e1, EcsChildOf, parent_1); ecs_add_pair(world, e2, EcsChildOf, parent_2); ecs_add(world, e1, Position); ecs_add(world, e2, Position); ecs_entity_t alert = ecs_alert(world, { .entity = ecs_new_entity(world, "position_without_velocity"), .filter.expr = "Position($this), ChildOf($this, $parent), !Position($parent)", .message = "$this: parent $parent does not have Position" }); test_assert(alert != 0); ecs_progress(world, 1.0); test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_assert(ecs_has(world, e2, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e2, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertError)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); test_str(instance->message, "p2.e2: parent p2 does not have Position"); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e2); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_fini(world); } void Alerts_alert_message_w_changed_var(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Position); ecs_entity_t parent_1 = ecs_new_entity(world, "p1"); ecs_add(world, parent_1, Position); ecs_entity_t parent_2 = ecs_new_entity(world, "p2"); ecs_entity_t parent_3 = ecs_new_entity(world, "p3"); ecs_entity_t e1 = ecs_new_entity(world, "e1"); ecs_entity_t e2 = ecs_new_entity(world, "e2"); ecs_add_pair(world, e1, EcsChildOf, parent_1); ecs_add_pair(world, e2, EcsChildOf, parent_2); ecs_add(world, e1, Position); ecs_add(world, e2, Position); ecs_entity_t alert = ecs_alert(world, { .entity = ecs_new_entity(world, "position_without_velocity"), .filter.expr = "Position($this), ChildOf($this, $parent), !Position($parent)", .message = "$this: parent $parent does not have Position" }); test_assert(alert != 0); ecs_progress(world, 1.0); test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_assert(ecs_has(world, e2, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e2, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertError)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); test_str(instance->message, "p2.e2: parent p2 does not have Position"); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e2); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_add_pair(world, e2, EcsChildOf, parent_3); ecs_progress(world, 1.0); test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_assert(ecs_has(world, e2, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e2, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); test_str(instance->message, "p3.e2: parent p3 does not have Position"); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e2); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_fini(world); } void Alerts_set_brief(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Position); ecs_entity_t alert = ecs_alert(world, { .entity = ecs_new_entity(world, "has_position"), .filter.expr = "Position", .brief = "Entity has Position" }); test_assert(alert != 0); test_str(ecs_doc_get_brief(world, alert), "Entity has Position"); ecs_fini(world); } void Alerts_set_doc_name(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Position); ecs_entity_t alert = ecs_alert(world, { .entity = ecs_new_entity(world, "has_position"), .filter.expr = "Position", .doc_name = "Has Position" }); test_assert(alert != 0); test_str(ecs_doc_get_name(world, alert), "Has Position"); ecs_fini(world); } void Alerts_alert_instance_has_doc_name(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t e2 = ecs_new_entity(world, "e2"); ecs_add(world, e2, Position); ecs_entity_t alert = ecs_alert(world, { .entity = ecs_new_entity(world, "position_without_velocity"), .filter.expr = "Position, !Velocity" }); test_assert(alert != 0); ecs_progress(world, 1.0); test_assert(ecs_has(world, e2, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e2, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertError)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e2); test_str(ecs_doc_get_name(world, it.entities[0]), "e2"); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_fini(world); } void Alerts_reraise_alert(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t e1 = ecs_new_entity(world, "e1"); ecs_add(world, e1, Position); ecs_entity_t alert = ecs_alert(world, { .entity = ecs_new_entity(world, "position_without_velocity"), .filter.expr = "Position, !Velocity" }); test_assert(alert != 0); ecs_progress(world, 1.0); test_assert(ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e1, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertError)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e1); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } /* Clear the alert */ ecs_add(world, e1, Velocity); ecs_progress(world, 1.0); /* Verify the alert has cleared */ test_assert(!ecs_has(world, e1, EcsAlertsActive)); /* Reraise the alert */ ecs_remove(world, e1, Velocity); ecs_progress(world, 1.0); /* Verify the alert has been raised again */ test_assert(ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e1, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertError)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e1); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_fini(world); } void Alerts_info_severity(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t e1 = ecs_new_entity(world, "e1"); ecs_add(world, e1, Position); ecs_entity_t alert = ecs_alert(world, { .entity = ecs_new_entity(world, "position_without_velocity"), .filter.expr = "Position, !Velocity", .severity = EcsAlertInfo }); test_assert(alert != 0); ecs_has_pair(world, alert, ecs_id(EcsAlert), EcsAlertInfo); ecs_progress(world, 1.0); test_assert(ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e1, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertInfo)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e1); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_fini(world); } void Alerts_warning_severity(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t e1 = ecs_new_entity(world, "e1"); ecs_add(world, e1, Position); ecs_entity_t alert = ecs_alert(world, { .entity = ecs_new_entity(world, "position_without_velocity"), .filter.expr = "Position, !Velocity", .severity = EcsAlertWarning }); test_assert(alert != 0); ecs_has_pair(world, alert, ecs_id(EcsAlert), EcsAlertWarning); ecs_progress(world, 1.0); test_assert(ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e1, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertWarning)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e1); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_fini(world); } void Alerts_error_severity(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t e1 = ecs_new_entity(world, "e1"); ecs_add(world, e1, Position); ecs_entity_t alert = ecs_alert(world, { .entity = ecs_new_entity(world, "position_without_velocity"), .filter.expr = "Position, !Velocity", .severity = EcsAlertError }); test_assert(alert != 0); ecs_has_pair(world, alert, ecs_id(EcsAlert), EcsAlertError); ecs_progress(world, 1.0); test_assert(ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e1, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertError)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e1); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_fini(world); } void Alerts_expire_after_retain(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t e1 = ecs_new_entity(world, "e1"); ecs_entity_t e2 = ecs_new_entity(world, "e2"); ecs_add(world, e1, Position); ecs_add(world, e1, Velocity); ecs_add(world, e2, Position); ecs_entity_t alert = ecs_alert(world, { .entity = ecs_new_entity(world, "position_without_velocity"), .filter.expr = "Position, !Velocity", .retain_period = 1.0 }); test_assert(alert != 0); ecs_progress(world, 1.0); ecs_entity_t ai = 0; test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_assert(ecs_has(world, e2, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e2, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertError)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e2); ai = it.entities[0]; test_assert(ecs_has(world, ai, EcsMetricValue)); test_int(ecs_get(world, ai, EcsMetricValue)->value, 1); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_progress(world, 1.0); /* Verify there is still only one alert */ test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_assert(ecs_has(world, e2, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); test_assert(ecs_is_alive(world, ai)); test_assert(ecs_has(world, ai, EcsMetricValue)); test_int(ecs_get(world, ai, EcsMetricValue)->value, 2); test_assert(ecs_get_alert_count(world, e2, alert) == 1); test_assert(!ecs_has_id(world, ai, EcsDisabled)); ecs_add(world, e2, Velocity); ecs_progress(world, 1.0); /* Verify alert hasn't cleared yet and is disabled */ test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_assert(!ecs_has(world, e2, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); test_assert(ecs_is_alive(world, ai)); test_assert(ecs_has(world, ai, EcsMetricValue)); test_int(ecs_get(world, ai, EcsMetricValue)->value, 2); test_assert(ecs_get_alert_count(world, e2, alert) == 0); test_assert(ecs_has_id(world, ai, EcsDisabled)); ecs_progress(world, 1.0); /* Verify that alert has cleared */ test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_assert(!ecs_has(world, e2, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 0); test_assert(ecs_get_alert_count(world, e2, alert) == 0); test_assert(!ecs_is_alive(world, ai)); ecs_fini(world); } void Alerts_revive_w_retain(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t e1 = ecs_new_entity(world, "e1"); ecs_entity_t e2 = ecs_new_entity(world, "e2"); ecs_add(world, e1, Position); ecs_add(world, e1, Velocity); ecs_add(world, e2, Position); ecs_entity_t alert = ecs_alert(world, { .entity = ecs_new_entity(world, "position_without_velocity"), .filter.expr = "Position, !Velocity", .retain_period = 1.0 }); test_assert(alert != 0); ecs_progress(world, 1.0); ecs_entity_t ai = 0; test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_assert(ecs_has(world, e2, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e2, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertError)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e2); ai = it.entities[0]; test_assert(ecs_has(world, ai, EcsMetricValue)); test_int(ecs_get(world, ai, EcsMetricValue)->value, 1); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_progress(world, 1.0); /* Verify there is still only one alert */ test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_assert(ecs_has(world, e2, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); test_assert(ecs_is_alive(world, ai)); test_assert(ecs_has(world, ai, EcsMetricValue)); test_int(ecs_get(world, ai, EcsMetricValue)->value, 2); test_assert(ecs_get_alert_count(world, e2, alert) == 1); test_assert(!ecs_has_id(world, ai, EcsDisabled)); ecs_add(world, e2, Velocity); ecs_progress(world, 1.0); /* Verify alert hasn't cleared yet and is disabled */ test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_assert(!ecs_has(world, e2, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); test_assert(ecs_is_alive(world, ai)); test_assert(ecs_has(world, ai, EcsMetricValue)); test_int(ecs_get(world, ai, EcsMetricValue)->value, 2); test_assert(ecs_get_alert_count(world, e2, alert) == 0); test_assert(ecs_has_id(world, ai, EcsDisabled)); ecs_remove(world, e2, Velocity); ecs_progress(world, 1.0); /* Verify that alert is active again */ test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_assert(ecs_has(world, e2, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); test_assert(ecs_is_alive(world, ai)); test_assert(ecs_has(world, ai, EcsMetricValue)); test_int(ecs_get(world, ai, EcsMetricValue)->value, 3); test_assert(ecs_get_alert_count(world, e2, alert) == 1); test_assert(!ecs_has_id(world, ai, EcsDisabled)); ecs_progress(world, 1.0); /* Verify that alert is still active */ test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_assert(ecs_has(world, e2, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); test_assert(ecs_is_alive(world, ai)); test_assert(ecs_has(world, ai, EcsMetricValue)); test_int(ecs_get(world, ai, EcsMetricValue)->value, 4); test_assert(ecs_get_alert_count(world, e2, alert) == 1); test_assert(!ecs_has_id(world, ai, EcsDisabled)); ecs_fini(world); } void Alerts_severity_filter(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); ecs_entity_t e1 = ecs_new_entity(world, "e1"); ecs_add(world, e1, Position); ecs_entity_t alert = ecs_alert(world, { .entity = ecs_new_entity(world, "position_without_velocity"), .filter.expr = "Position, !Velocity", .severity_filters[0] = { .severity = EcsAlertWarning, .with = ecs_id(Mass) } }); test_assert(alert != 0); ecs_has_pair(world, alert, ecs_id(EcsAlert), EcsAlertError); ecs_progress(world, 1.0); test_assert(ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e1, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertError)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e1); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_add(world, e1, Mass); ecs_progress(world, 1.0); test_assert(ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e1, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertWarning)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e1); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_remove(world, e1, Mass); ecs_progress(world, 1.0); test_assert(ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e1, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertError)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e1); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_fini(world); } void Alerts_two_severity_filters(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); ECS_COMPONENT(world, Rotation); ecs_entity_t e1 = ecs_new_entity(world, "e1"); ecs_add(world, e1, Position); ecs_entity_t alert = ecs_alert(world, { .entity = ecs_new_entity(world, "position_without_velocity"), .filter.expr = "Position, !Velocity", .severity_filters[0] = { .severity = EcsAlertWarning, .with = ecs_id(Mass) }, .severity_filters[1] = { .severity = EcsAlertInfo, .with = ecs_id(Rotation) } }); test_assert(alert != 0); ecs_has_pair(world, alert, ecs_id(EcsAlert), EcsAlertError); ecs_progress(world, 1.0); test_assert(ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e1, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertError)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e1); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_add(world, e1, Mass); ecs_progress(world, 1.0); test_assert(ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e1, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertWarning)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e1); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_remove(world, e1, Mass); ecs_add(world, e1, Rotation); ecs_progress(world, 1.0); test_assert(ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e1, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertInfo)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e1); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_remove(world, e1, Rotation); ecs_progress(world, 1.0); test_assert(ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e1, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertError)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e1); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_fini(world); } void Alerts_severity_filter_w_var(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); ecs_entity_t parent = ecs_new_id(world); ecs_entity_t e1 = ecs_new_entity(world, "e1"); ecs_add_pair(world, e1, EcsChildOf, parent); ecs_add(world, e1, Position); ecs_add(world, e1, Mass); ecs_entity_t alert = ecs_alert(world, { .entity = ecs_new_entity(world, "position_without_velocity"), .filter.expr = "Position, !Velocity, (ChildOf, $parent)", .severity_filters[0] = { .severity = EcsAlertWarning, .with = ecs_id(Mass), .var = "parent" } }); test_assert(alert != 0); ecs_has_pair(world, alert, ecs_id(EcsAlert), EcsAlertError); ecs_progress(world, 1.0); test_assert(ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e1, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertError)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e1); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_add(world, parent, Mass); ecs_progress(world, 1.0); test_assert(ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e1, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertWarning)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e1); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_remove(world, parent, Mass); ecs_progress(world, 1.0); test_assert(ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e1, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertError)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e1); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_fini(world); } void Alerts_severity_filter_w_var_change_var(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); ecs_entity_t parent_1 = ecs_new_id(world); ecs_entity_t parent_2 = ecs_new_id(world); ecs_add(world, parent_2, Mass); ecs_entity_t e1 = ecs_new_entity(world, "e1"); ecs_add_pair(world, e1, EcsChildOf, parent_1); ecs_add(world, e1, Position); ecs_add(world, e1, Mass); ecs_entity_t alert = ecs_alert(world, { .entity = ecs_new_entity(world, "position_without_velocity"), .filter.expr = "Position, !Velocity, (ChildOf, $parent)", .severity_filters[0] = { .severity = EcsAlertWarning, .with = ecs_id(Mass), .var = "parent" } }); test_assert(alert != 0); ecs_has_pair(world, alert, ecs_id(EcsAlert), EcsAlertError); ecs_progress(world, 1.0); test_assert(ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e1, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertError)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e1); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_add_pair(world, e1, EcsChildOf, parent_2); ecs_progress(world, 1.0); test_assert(ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e1, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertWarning)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e1); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_add_pair(world, e1, EcsChildOf, parent_1); ecs_progress(world, 1.0); test_assert(ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e1, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertError)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e1); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_fini(world); } void Alerts_member_range_warning(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Mass); ecs_struct(world, { .entity = ecs_id(Mass), .members = {{ "value", ecs_id(ecs_f32_t), .warning_range = { 0, 100 }}} }); ecs_entity_t e1 = ecs_new_entity(world, "e1"); ecs_set(world, e1, Mass, {50}); ecs_entity_t member = ecs_lookup_fullpath(world, "Mass.value"); test_assert(member != 0); ecs_entity_t alert = ecs_alert(world, { .entity = ecs_new_entity(world, "high_mass"), .filter.expr = "Mass", .member = member }); test_assert(alert != 0); ecs_progress(world, 1.0); test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 0); ecs_set(world, e1, Mass, {150}); ecs_progress(world, 1.0); test_assert(ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e1, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertWarning)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e1); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_set(world, e1, Mass, {25}); ecs_progress(world, 1.0); test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 0); ecs_fini(world); } void Alerts_member_range_error(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Mass); ecs_struct(world, { .entity = ecs_id(Mass), .members = {{ "value", ecs_id(ecs_f32_t), .error_range = { 0, 100 }}} }); ecs_entity_t e1 = ecs_new_entity(world, "e1"); ecs_set(world, e1, Mass, {50}); ecs_entity_t member = ecs_lookup_fullpath(world, "Mass.value"); test_assert(member != 0); ecs_entity_t alert = ecs_alert(world, { .entity = ecs_new_entity(world, "high_mass"), .filter.expr = "Mass", .member = member }); test_assert(alert != 0); ecs_progress(world, 1.0); test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 0); ecs_set(world, e1, Mass, {150}); ecs_progress(world, 1.0); test_assert(ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e1, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertError)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e1); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_set(world, e1, Mass, {25}); ecs_progress(world, 1.0); test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 0); ecs_fini(world); } void Alerts_member_range_warning_error(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Mass); ecs_struct(world, { .entity = ecs_id(Mass), .members = {{ "value", ecs_id(ecs_f32_t), .warning_range = {0, 50}, .error_range = { 0, 100 }}} }); ecs_entity_t e1 = ecs_new_entity(world, "e1"); ecs_set(world, e1, Mass, {25}); ecs_entity_t member = ecs_lookup_fullpath(world, "Mass.value"); test_assert(member != 0); ecs_entity_t alert = ecs_alert(world, { .entity = ecs_new_entity(world, "high_mass"), .filter.expr = "Mass", .member = member }); test_assert(alert != 0); ecs_progress(world, 1.0); test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 0); ecs_set(world, e1, Mass, {75}); ecs_progress(world, 1.0); test_assert(ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e1, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertWarning)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e1); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_set(world, e1, Mass, {125}); ecs_progress(world, 1.0); test_assert(ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e1, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertError)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e1); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_set(world, e1, Mass, {25}); ecs_progress(world, 1.0); test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 0); ecs_fini(world); } void Alerts_member_range_error_w_warning_severity(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Mass); ecs_struct(world, { .entity = ecs_id(Mass), .members = {{ "value", ecs_id(ecs_f32_t), .error_range = { 0, 100 }}} }); ecs_entity_t e1 = ecs_new_entity(world, "e1"); ecs_set(world, e1, Mass, {50}); ecs_entity_t member = ecs_lookup_fullpath(world, "Mass.value"); test_assert(member != 0); ecs_entity_t alert = ecs_alert(world, { .entity = ecs_new_entity(world, "high_mass"), .filter.expr = "Mass", .severity = EcsAlertWarning, .member = member }); test_assert(alert != 0); ecs_progress(world, 1.0); test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 0); ecs_set(world, e1, Mass, {150}); ecs_progress(world, 1.0); test_assert(ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e1, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertWarning)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e1); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_set(world, e1, Mass, {25}); ecs_progress(world, 1.0); test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 0); ecs_fini(world); } void Alerts_member_range_error_w_severity_filter(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Mass); ECS_TAG(world, Tag); ecs_struct(world, { .entity = ecs_id(Mass), .members = {{ "value", ecs_id(ecs_f32_t), .error_range = { 0, 100 }}} }); ecs_entity_t e1 = ecs_new_entity(world, "e1"); ecs_set(world, e1, Mass, {50}); ecs_entity_t member = ecs_lookup_fullpath(world, "Mass.value"); test_assert(member != 0); ecs_entity_t alert = ecs_alert(world, { .entity = ecs_new_entity(world, "high_mass"), .filter.expr = "Mass", .severity = EcsAlertWarning, .severity_filters[0] = { .with = Tag, .severity = EcsAlertError }, .member = member }); test_assert(alert != 0); ecs_progress(world, 1.0); test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 0); ecs_set(world, e1, Mass, {150}); ecs_progress(world, 1.0); test_assert(ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e1, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertWarning)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e1); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_add(world, e1, Tag); ecs_progress(world, 1.0); test_assert(ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e1, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertError)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e1); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_fini(world); } void Alerts_member_range_warning_w_severity_filter(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Mass); ECS_TAG(world, Tag); ecs_struct(world, { .entity = ecs_id(Mass), .members = {{ "value", ecs_id(ecs_f32_t), .warning_range = { 0, 100 }}} }); ecs_entity_t e1 = ecs_new_entity(world, "e1"); ecs_set(world, e1, Mass, {50}); ecs_entity_t member = ecs_lookup_fullpath(world, "Mass.value"); test_assert(member != 0); ecs_entity_t alert = ecs_alert(world, { .entity = ecs_new_entity(world, "high_mass"), .filter.expr = "Mass", .severity = EcsAlertWarning, .severity_filters[0] = { .with = Tag, .severity = EcsAlertError }, .member = member }); test_assert(alert != 0); ecs_progress(world, 1.0); test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 0); ecs_set(world, e1, Mass, {150}); ecs_progress(world, 1.0); test_assert(ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e1, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertWarning)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e1); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_add(world, e1, Tag); ecs_progress(world, 1.0); test_assert(ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e1, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertWarning)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e1); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_fini(world); } void Alerts_member_range_pair_id(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Mass); ECS_TAG(world, Tag); ecs_struct(world, { .entity = ecs_id(Mass), .members = {{ "value", ecs_id(ecs_f32_t), .error_range = { 0, 100 }}} }); ecs_entity_t e1 = ecs_new_entity(world, "e1"); ecs_set(world, e1, Mass, {50}); ecs_entity_t member = ecs_lookup_fullpath(world, "Mass.value"); test_assert(member != 0); ecs_entity_t alert = ecs_alert(world, { .entity = ecs_new_entity(world, "high_mass"), .filter.expr = "Mass", .member = member, .id = ecs_pair_t(Mass, Tag) }); test_assert(alert != 0); ecs_progress(world, 1.0); test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 0); ecs_set_pair(world, e1, Mass, Tag, {150}); ecs_progress(world, 1.0); test_assert(ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e1, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertError)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e1); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_set_pair(world, e1, Mass, Tag, {25}); ecs_progress(world, 1.0); test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 0); ecs_fini(world); } void Alerts_member_range_invalid_member(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Mass); ECS_TAG(world, Tag); ecs_struct(world, { .entity = ecs_id(Mass), .members = {{ "value", ecs_id(ecs_f32_t), .error_range = { 0, 100 }}} }); ecs_entity_t member = ecs_lookup_fullpath(world, "Mass.value"); test_assert(member != 0); ecs_log_set_level(-4); ecs_entity_t alert = ecs_alert(world, { .entity = ecs_new_entity(world, "high_mass"), .filter.expr = "Mass", .member = Tag, }); test_assert(alert == 0); ecs_fini(world); } void Alerts_member_range_invalid_member_child(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Mass); ecs_struct(world, { .entity = ecs_id(Mass), .members = {{ "value", ecs_id(ecs_f32_t), .error_range = { 0, 100 }}} }); ecs_entity_t member = ecs_lookup_fullpath(world, "Mass.value"); test_assert(member != 0); ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, ecs_id(Mass)); ecs_log_set_level(-4); ecs_entity_t alert = ecs_alert(world, { .entity = ecs_new_entity(world, "high_mass"), .filter.expr = "Mass", .member = child, }); test_assert(alert == 0); ecs_fini(world); } void Alerts_member_range_invalid_type(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Mass); ECS_TAG(world, Tag); ecs_struct(world, { .entity = ecs_id(Mass), .members = {{ "value", ecs_id(ecs_f32_t), .error_range = { 0, 100 }}} }); ecs_entity_t member = ecs_lookup_fullpath(world, "Mass.value"); test_assert(member != 0); ecs_log_set_level(-4); ecs_entity_t alert = ecs_alert(world, { .entity = ecs_new_entity(world, "high_mass"), .filter.expr = "Mass", .member = member, .id = Tag }); test_assert(alert == 0); ecs_fini(world); } void Alerts_member_range_invalid_member_type(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Mass); ecs_struct(world, { .entity = ecs_id(Mass), .members = {{ "value", ecs_id(ecs_f32_t) }} }); ecs_struct(world, { .entity = ecs_new_entity(world, "Foo"), .members = {{ "value", ecs_id(Mass) }} }); ecs_entity_t member = ecs_lookup_fullpath(world, "Foo.value"); test_assert(member != 0); ecs_log_set_level(-4); ecs_entity_t alert = ecs_alert(world, { .entity = ecs_new_entity(world, "high_mass"), .filter.expr = "Mass", .member = member, }); test_assert(alert == 0); ecs_fini(world); } void Alerts_member_range_no_range(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Mass); ecs_struct(world, { .entity = ecs_id(Mass), .members = {{ "value", ecs_id(ecs_f32_t) }} }); ecs_entity_t member = ecs_lookup_fullpath(world, "Mass.value"); test_assert(member != 0); ecs_log_set_level(-4); ecs_entity_t alert = ecs_alert(world, { .entity = ecs_new_entity(world, "high_mass"), .filter.expr = "Mass", .member = member, }); test_assert(alert == 0); ecs_fini(world); } void Alerts_member_range_alert_two_instances(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Mass); ecs_struct(world, { .entity = ecs_id(Mass), .members = {{ "value", ecs_id(ecs_f32_t), .error_range = { 0, 100 }}} }); ecs_entity_t e1 = ecs_new_entity(world, "e1"); ecs_set(world, e1, Mass, {50}); ecs_entity_t e2 = ecs_new_entity(world, "e2"); ecs_set(world, e2, Mass, {50}); ecs_entity_t member = ecs_lookup_fullpath(world, "Mass.value"); test_assert(member != 0); ecs_entity_t alert = ecs_alert(world, { .entity = ecs_new_entity(world, "high_mass"), .filter.expr = "Mass", .member = member, }); test_assert(alert != 0); ecs_progress(world, 1.0); test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_assert(!ecs_has(world, e2, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 0); ecs_set(world, e1, Mass, {150}); ecs_set(world, e2, Mass, {150}); ecs_progress(world, 1.0); test_assert(ecs_has(world, e1, EcsAlertsActive)); test_assert(ecs_has(world, e2, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 2); { test_assert(ecs_get_alert_count(world, e1, alert) == 1); test_assert(ecs_get_alert_count(world, e2, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 2); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertError)); test_assert(ecs_get(world, it.entities[0], EcsAlertInstance) != NULL); test_assert(ecs_get(world, it.entities[1], EcsAlertInstance) != NULL); { const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e1); } { const EcsMetricSource *source = ecs_get(world, it.entities[1], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e2); } test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_progress(world, 1.0); test_assert(ecs_has(world, e1, EcsAlertsActive)); test_assert(ecs_has(world, e2, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 2); { test_assert(ecs_get_alert_count(world, e1, alert) == 1); test_assert(ecs_get_alert_count(world, e2, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 2); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertError)); test_assert(ecs_get(world, it.entities[0], EcsAlertInstance) != NULL); test_assert(ecs_get(world, it.entities[1], EcsAlertInstance) != NULL); { const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e1); } { const EcsMetricSource *source = ecs_get(world, it.entities[1], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e2); } test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_set(world, e1, Mass, {25}); ecs_set(world, e2, Mass, {25}); ecs_progress(world, 1.0); test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 0); ecs_fini(world); } void Alerts_member_range_from_var(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Mass); ecs_struct(world, { .entity = ecs_id(Mass), .members = {{ "value", ecs_id(ecs_f32_t), .error_range = { 0, 100 }}} }); ecs_entity_t p = ecs_new_entity(world, "p"); ecs_set(world, p, Mass, {50}); ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p); ecs_entity_t member = ecs_lookup_fullpath(world, "Mass.value"); test_assert(member != 0); ecs_entity_t alert = ecs_alert(world, { .entity = ecs_new_entity(world, "high_parent_mass"), .filter.expr = "(ChildOf, $parent), Mass($parent)", .member = member, .var = "parent" }); test_assert(alert != 0); ecs_progress(world, 1.0); test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 0); ecs_set(world, p, Mass, {150}); ecs_progress(world, 1.0); test_assert(ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e1, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertError)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e1); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_set(world, p, Mass, {25}); ecs_progress(world, 1.0); test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 0); ecs_fini(world); } void Alerts_member_range_from_var_after_remove(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Mass); ecs_struct(world, { .entity = ecs_id(Mass), .members = {{ "value", ecs_id(ecs_f32_t), .error_range = { 0, 100 }}} }); ecs_entity_t p = ecs_new_entity(world, "p"); ecs_set(world, p, Mass, {50}); ecs_entity_t e1 = ecs_new_w_pair(world, EcsChildOf, p); ecs_entity_t member = ecs_lookup_fullpath(world, "Mass.value"); test_assert(member != 0); ecs_entity_t alert = ecs_alert(world, { .entity = ecs_new_entity(world, "high_parent_mass"), .filter.expr = "(ChildOf, $parent), Mass($parent)", .member = member, .var = "parent" }); test_assert(alert != 0); ecs_progress(world, 1.0); test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 0); ecs_set(world, p, Mass, {150}); ecs_progress(world, 1.0); test_assert(ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e1, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] != 0); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertError)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e1); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_remove(world, p, Mass); ecs_progress(world, 1.0); test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 0); ecs_fini(world); } void Alerts_retained_alert_w_dead_source(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ecs_entity_t e1 = ecs_new_entity(world, "e1"); ecs_add(world, e1, Position); ecs_entity_t alert = ecs_alert(world, { .entity = ecs_new_entity(world, "position_without_velocity"), .filter.expr = "Position, !Velocity", .severity = EcsAlertError, .retain_period = 10 }); test_assert(alert != 0); ecs_has_pair(world, alert, ecs_id(EcsAlert), EcsAlertError); ecs_progress(world, 1.0); test_assert(ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); ecs_entity_t ai = 0; { test_assert(ecs_get_alert_count(world, e1, alert) == 1); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); ai = it.entities[0]; test_assert(ai != 0); test_assert(ecs_get_parent(world, ai) == alert); test_assert(ecs_has_pair(world, ai, ecs_id(EcsAlert), EcsAlertError)); const EcsAlertInstance *instance = ecs_get(world, ai, EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, ai, EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e1); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_add(world, e1, Velocity); ecs_progress(world, 1.0); test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e1, alert) == 0); ecs_filter_t *alerts = ecs_filter(world, { .expr = "flecs.alerts.Instance, ?Disabled" }); ecs_iter_t it = ecs_filter_iter(world, alerts); test_bool(ecs_filter_next(&it), true); test_int(it.count, 1); test_assert(it.entities[0] == ai); test_assert(ecs_get_parent(world, it.entities[0]) == alert); test_assert(ecs_has_pair(world, it.entities[0], ecs_id(EcsAlert), EcsAlertError)); const EcsAlertInstance *instance = ecs_get(world, it.entities[0], EcsAlertInstance); test_assert(instance != NULL); const EcsMetricSource *source = ecs_get(world, it.entities[0], EcsMetricSource); test_assert(source != NULL); test_int(source->entity, e1); test_bool(ecs_filter_next(&it), false); ecs_filter_fini(alerts); } ecs_delete(world, e1); ecs_progress(world, 1.0); test_int(ecs_count(world, EcsAlertInstance), 0); ecs_fini(world); } void Alerts_alert_counts(void) { ecs_world_t *world = ecs_init(); ECS_IMPORT(world, FlecsAlerts); ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); ECS_COMPONENT(world, Mass); ecs_entity_t e1 = ecs_new_entity(world, "e1"); ecs_add(world, e1, Position); ecs_entity_t alert_1 = ecs_alert(world, { .entity = ecs_new_entity(world, "position_without_velocity"), .filter.expr = "Position, !Velocity" }); test_assert(alert_1 != 0); ecs_entity_t alert_2 = ecs_alert(world, { .entity = ecs_new_entity(world, "position_without_mass"), .filter.expr = "Position, !Mass", .severity = EcsAlertWarning }); test_assert(alert_2 != 0); ecs_progress(world, 1.0); test_assert(ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 2); { test_assert(ecs_get_alert_count(world, e1, alert_1) == 1); test_assert(ecs_get_alert_count(world, e1, alert_2) == 1); const EcsAlertsActive *active = ecs_get(world, e1, EcsAlertsActive); test_assert(active != NULL); test_int(active->error_count, 1); test_int(active->warning_count, 1); } ecs_add(world, e1, Velocity); ecs_progress(world, 1.0); test_assert(ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 1); { test_assert(ecs_get_alert_count(world, e1, alert_1) == 0); test_assert(ecs_get_alert_count(world, e1, alert_2) == 1); const EcsAlertsActive *active = ecs_get(world, e1, EcsAlertsActive); test_assert(active != NULL); test_int(active->error_count, 0); test_int(active->warning_count, 1); } ecs_add(world, e1, Mass); ecs_progress(world, 1.0); /* Verify that alerts have cleared */ test_assert(!ecs_has(world, e1, EcsAlertsActive)); test_int(ecs_count(world, EcsAlertInstance), 0); ecs_fini(world); }